| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- import Foundation
- import UIKit
- import MetalKit
- import AVFoundation
- import ObjectiveC
- @objcMembers
- public final class LNVAPLogger: NSObject {
- public typealias LNLogHandler = (_ level: Int, _ file: String, _ line: Int, _ function: String, _ module: String, _ message: String) -> Void
- private static let lock = NSRecursiveLock()
- private static var handler: LNLogHandler?
- @objc(registerLogHandler:)
- public static func registerLogHandler(_ handler: @escaping LNLogHandler) {
- lock.lock()
- self.handler = handler
- lock.unlock()
- }
- @objc(clearLogHandler)
- public static func clearLogHandler() {
- lock.lock()
- handler = nil
- lock.unlock()
- }
- @objc(logLevel:file:line:func:module:message:)
- public static func log(level: Int, file: String, line: Int, func function: String, module: String, message: String) {
- let sanitized = message.replacingOccurrences(of: "%", with: "")
- lock.lock()
- let current = handler
- lock.unlock()
- if let current {
- current(level, file, line, function, module, sanitized)
- return
- }
- #if DEBUG
- let fileName = (file as NSString).lastPathComponent
- NSLog("<\(level)> \(fileName)(\(line)):\(function) [\(module)] - \(sanitized)")
- #endif
- }
- }
- @objcMembers
- public final class LNVAPSafeMutableArray: NSObject {
- private let lock = NSRecursiveLock()
- private var storage: NSMutableArray
- public override init() {
- self.storage = NSMutableArray()
- super.init()
- }
- @objc(initWithCapacity:)
- public init(capacity: Int) {
- self.storage = NSMutableArray(capacity: capacity)
- super.init()
- }
- @objc(initWithArray:)
- public init(array: [Any]) {
- self.storage = NSMutableArray(array: array)
- super.init()
- }
- @objc(count)
- public var count: Int {
- lock.lock()
- defer { lock.unlock() }
- return storage.count
- }
- @objc(objectAtIndex:)
- public func object(at index: Int) -> Any {
- lock.lock()
- defer { lock.unlock() }
- return storage[index]
- }
- @objc(firstObject)
- public var firstObject: Any? {
- lock.lock()
- defer { lock.unlock() }
- return storage.firstObject
- }
- @objc(lastObject)
- public var lastObject: Any? {
- lock.lock()
- defer { lock.unlock() }
- return storage.lastObject
- }
- @objc(addObject:)
- public func add(_ object: Any) {
- lock.lock()
- storage.add(object)
- lock.unlock()
- }
- @objc(insertObject:atIndex:)
- public func insert(_ object: Any, at index: Int) {
- lock.lock()
- storage.insert(object, at: index)
- lock.unlock()
- }
- @objc(removeObjectAtIndex:)
- public func removeObject(at index: Int) {
- lock.lock()
- storage.removeObject(at: index)
- lock.unlock()
- }
- @objc(removeLastObject)
- public func removeLastObject() {
- lock.lock()
- storage.removeLastObject()
- lock.unlock()
- }
- @objc(removeAllObjects)
- public func removeAllObjects() {
- lock.lock()
- storage.removeAllObjects()
- lock.unlock()
- }
- @objc(containsObject:)
- public func contains(_ object: Any) -> Bool {
- lock.lock()
- defer { lock.unlock() }
- return storage.contains(object)
- }
- @objc(allObjects)
- public func allObjects() -> [Any] {
- lock.lock()
- defer { lock.unlock() }
- return storage.copy() as? [Any] ?? []
- }
- }
- @objcMembers
- public final class LNVAPSafeMutableDictionary: NSObject {
- private let lock = NSRecursiveLock()
- private var storage: NSMutableDictionary
- public override init() {
- self.storage = NSMutableDictionary()
- super.init()
- }
- @objc(initWithCapacity:)
- public init(capacity: Int) {
- self.storage = NSMutableDictionary(capacity: capacity)
- super.init()
- }
- @objc(initWithDictionary:)
- public init(dictionary: [AnyHashable: Any]) {
- self.storage = NSMutableDictionary(dictionary: dictionary)
- super.init()
- }
- @objc(count)
- public var count: Int {
- lock.lock()
- defer { lock.unlock() }
- return storage.count
- }
- @objc(objectForKey:)
- public func object(forKey key: NSCopying) -> Any? {
- lock.lock()
- defer { lock.unlock() }
- return storage.object(forKey: key)
- }
- @objc(setObject:forKey:)
- public func setObject(_ object: Any, forKey key: NSCopying) {
- lock.lock()
- storage.setObject(object, forKey: key)
- lock.unlock()
- }
- @objc(removeObjectForKey:)
- public func removeObject(forKey key: NSCopying) {
- lock.lock()
- storage.removeObject(forKey: key)
- lock.unlock()
- }
- @objc(removeAllObjects)
- public func removeAllObjects() {
- lock.lock()
- storage.removeAllObjects()
- lock.unlock()
- }
- @objc(allKeys)
- public var allKeys: [Any] {
- lock.lock()
- defer { lock.unlock() }
- return storage.allKeys
- }
- @objc(allValues)
- public var allValues: [Any] {
- lock.lock()
- defer { lock.unlock() }
- return storage.allValues
- }
- @objc(dictionaryRepresentation)
- public func dictionaryRepresentation() -> [AnyHashable: Any] {
- lock.lock()
- defer { lock.unlock() }
- return storage.copy() as? [AnyHashable: Any] ?? [:]
- }
- }
- @objcMembers
- public final class LNVAPWeakProxy: NSObject {
- private weak var target: NSObjectProtocol?
- @objc(initWithTarget:)
- public init(target: NSObjectProtocol?) {
- self.target = target
- super.init()
- }
- @objc(proxyWithTarget:)
- public static func proxy(with target: NSObjectProtocol?) -> LNVAPWeakProxy {
- LNVAPWeakProxy(target: target)
- }
- public override func forwardingTarget(for aSelector: Selector!) -> Any? {
- target
- }
- public override func responds(to aSelector: Selector!) -> Bool {
- target?.responds(to: aSelector) ?? false
- }
- }
- @objcMembers
- public final class LNVAPMetalShaderFunctionLoader: NSObject {
- private let device: MTLDevice
- private var defaultLibrary: MTLLibrary?
- @objc(initWithDevice:)
- public init(device: MTLDevice) {
- self.device = device
- super.init()
- }
- @objc(loadFunctionWithName:)
- public func loadFunction(withName functionName: String) -> MTLFunction? {
- if defaultLibrary == nil {
- if let path = Bundle(for: Self.self).path(forResource: "default", ofType: "metallib") {
- defaultLibrary = try? device.makeLibrary(filepath: path)
- }
- if defaultLibrary == nil {
- defaultLibrary = device.makeDefaultLibrary()
- }
- }
- return defaultLibrary?.makeFunction(name: functionName)
- }
- }
- @objcMembers
- public final class LNVAPMetalUtil: NSObject {
- public static let vapAttachmentVertexFunctionName = "vapAttachment_vertexShader"
- public static let vapAttachmentFragmentFunctionName = "vapAttachment_FragmentShader"
- public static let vapVertexFunctionName = "vap_vertexShader"
- public static let vapYUVFragmentFunctionName = "vap_yuvFragmentShader"
- public static let vapMaskFragmentFunctionName = "vap_maskFragmentShader"
- public static let vapMaskBlurFragmentFunctionName = "vap_maskBlurFragmentShader"
- public static let vapMTLVerticesIdentity: [Float] = [-1.0, -1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0]
- public static let vapMTLTextureCoordinatesIdentity: [Float] = [0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]
- public static let vapMTLTextureCoordinatesFor90: [Float] = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]
- @objc(genVerticesWithRect:containerSize:reverse:)
- public static func genVertices(rect: CGRect, containerSize: CGSize, reverse: Bool) -> [Float] {
- guard containerSize.width > 0, containerSize.height > 0 else { return vapMTLVerticesIdentity }
- let originX = Float(-1 + 2 * rect.origin.x / containerSize.width)
- let originY = Float(1 - 2 * rect.origin.y / containerSize.height)
- let width = Float(2 * rect.size.width / containerSize.width)
- let height = Float(2 * rect.size.height / containerSize.height)
- if reverse {
- return [originX, originY - height, 0.0, 1.0,
- originX, originY, 0.0, 1.0,
- originX + width, originY - height, 0.0, 1.0,
- originX + width, originY, 0.0, 1.0]
- }
- return [originX, originY, 0.0, 1.0,
- originX, originY - height, 0.0, 1.0,
- originX + width, originY, 0.0, 1.0,
- originX + width, originY - height, 0.0, 1.0]
- }
- @objc(genTextureCoordinatesWithRect:containerSize:reverse:degree:)
- public static func genTextureCoordinates(rect: CGRect, containerSize: CGSize, reverse: Bool, degree: Int) -> [Float] {
- _ = degree
- guard containerSize.width > 0, containerSize.height > 0 else { return vapMTLTextureCoordinatesIdentity }
- let originX = Float(rect.origin.x / containerSize.width)
- let originY = Float(rect.origin.y / containerSize.height)
- let width = Float(rect.size.width / containerSize.width)
- let height = Float(rect.size.height / containerSize.height)
- if reverse {
- return [originX, originY,
- originX, originY + height,
- originX + width, originY,
- originX + width, originY + height]
- }
- return [originX, originY + height,
- originX, originY,
- originX + width, originY + height,
- originX + width, originY]
- }
- @objc(sourceSizeForCenterFullWithSourceSize:renderSize:)
- public static func sourceSizeForCenterFull(sourceSize: CGSize, renderSize: CGSize) -> CGSize {
- if sourceSize.width >= renderSize.width, sourceSize.height >= renderSize.height {
- return sourceSize
- }
- return rect(inside: CGRect(origin: .zero, size: renderSize), aspectRatio: sourceSize, contentMode: .scaleAspectFill).size
- }
- @objc(rectForCenterFullWithSourceSize:renderSize:)
- public static func rectForCenterFull(sourceSize: CGSize, renderSize: CGSize) -> CGRect {
- if sourceSize.width >= renderSize.width, sourceSize.height >= renderSize.height {
- return CGRect(x: (sourceSize.width - renderSize.width) / 2.0,
- y: (sourceSize.height - renderSize.height) / 2.0,
- width: renderSize.width,
- height: renderSize.height)
- }
- let fill = rect(inside: CGRect(origin: .zero, size: renderSize), aspectRatio: sourceSize, contentMode: .scaleAspectFill)
- return CGRect(x: -fill.origin.x, y: -fill.origin.y, width: renderSize.width, height: renderSize.height)
- }
- @objc(rectInsideBoundingRect:aspectRatio:contentMode:)
- public static func rect(inside boundingRect: CGRect, aspectRatio: CGSize, contentMode: UIView.ContentMode) -> CGRect {
- guard aspectRatio.width > 0, aspectRatio.height > 0 else { return boundingRect }
- switch contentMode {
- case .scaleToFill:
- return boundingRect
- case .scaleAspectFit:
- return AVMakeRect(aspectRatio: aspectRatio, insideRect: boundingRect)
- case .scaleAspectFill:
- let ratio = max(boundingRect.width / aspectRatio.width, boundingRect.height / aspectRatio.height)
- let size = CGSize(width: aspectRatio.width * ratio, height: aspectRatio.height * ratio)
- return CGRect(x: boundingRect.origin.x + (boundingRect.width - size.width) / 2.0,
- y: boundingRect.origin.y + (boundingRect.height - size.height) / 2.0,
- width: size.width,
- height: size.height)
- case .center:
- return CGRect(x: boundingRect.origin.x + (boundingRect.width - aspectRatio.width) / 2.0,
- y: boundingRect.origin.y + (boundingRect.height - aspectRatio.height) / 2.0,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .top:
- return CGRect(x: boundingRect.origin.x + (boundingRect.width - aspectRatio.width) / 2.0,
- y: boundingRect.origin.y,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .bottom:
- return CGRect(x: boundingRect.origin.x + (boundingRect.width - aspectRatio.width) / 2.0,
- y: boundingRect.maxY - aspectRatio.height,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .left:
- return CGRect(x: boundingRect.origin.x,
- y: boundingRect.origin.y + (boundingRect.height - aspectRatio.height) / 2.0,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .right:
- return CGRect(x: boundingRect.maxX - aspectRatio.width,
- y: boundingRect.origin.y + (boundingRect.height - aspectRatio.height) / 2.0,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .topLeft:
- return CGRect(origin: boundingRect.origin, size: aspectRatio)
- case .topRight:
- return CGRect(x: boundingRect.maxX - aspectRatio.width,
- y: boundingRect.origin.y,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .bottomLeft:
- return CGRect(x: boundingRect.origin.x,
- y: boundingRect.maxY - aspectRatio.height,
- width: aspectRatio.width,
- height: aspectRatio.height)
- case .bottomRight:
- return CGRect(x: boundingRect.maxX - aspectRatio.width,
- y: boundingRect.maxY - aspectRatio.height,
- width: aspectRatio.width,
- height: aspectRatio.height)
- default:
- return boundingRect
- }
- }
- }
- public extension UIView {
- @objc func lnStopVAPIfNeeded() {
- stopHWDMP4()
- }
- }
- @objcMembers
- public final class LNVAPDeviceUtil: NSObject {
- private static var cachedSystemVersion: Double = (UIDevice.current.systemVersion as NSString).doubleValue
- public static var systemVersionNum: Double {
- cachedSystemVersion
- }
- public static var defaultMTLResourceOption: MTLResourceOptions {
- if #available(iOS 9.0, *) {
- return .storageModeShared
- }
- return []
- }
- }
- public extension NSArray {
- @objc(ln_rectValue)
- func ln_rectValue() -> CGRect {
- guard count >= 4 else { return .zero }
- for index in 0..<4 {
- let value = self[index]
- if !(value is NSNumber) && !(value is NSString) {
- return .zero
- }
- }
- return CGRect(x: (self[0] as? NSNumber)?.doubleValue ?? ((self[0] as? NSString)?.doubleValue ?? 0),
- y: (self[1] as? NSNumber)?.doubleValue ?? ((self[1] as? NSString)?.doubleValue ?? 0),
- width: (self[2] as? NSNumber)?.doubleValue ?? ((self[2] as? NSString)?.doubleValue ?? 0),
- height: (self[3] as? NSNumber)?.doubleValue ?? ((self[3] as? NSString)?.doubleValue ?? 0))
- }
- }
- public extension NSDictionary {
- @objc(ln_floatValueForKey:)
- func ln_floatValue(forKey key: String?) -> CGFloat {
- guard let key else { return 0 }
- guard let value = self[key], !(value is NSNull) else { return 0 }
- if let number = value as? NSNumber { return CGFloat(number.doubleValue) }
- if let string = value as? NSString { return CGFloat(string.doubleValue) }
- return 0
- }
- @objc(ln_integerValueForKey:)
- func ln_integerValue(forKey key: String?) -> Int {
- guard let key else { return 0 }
- guard let value = self[key], !(value is NSNull) else { return 0 }
- if let number = value as? NSNumber { return number.intValue }
- if let string = value as? NSString { return string.integerValue }
- return 0
- }
- @objc(ln_stringValueForKey:)
- func ln_stringValue(forKey key: String?) -> String {
- guard let key else { return "" }
- guard let value = self[key], !(value is NSNull) else { return "" }
- if let string = value as? String { return string }
- if let number = value as? NSNumber { return number.description }
- return ""
- }
- @objc(ln_dictionaryValueForKey:)
- func ln_dictionaryValue(forKey key: String?) -> NSDictionary? {
- guard let key else { return nil }
- return self[key] as? NSDictionary
- }
- @objc(ln_arrayValueForKey:)
- func ln_arrayValue(forKey key: String?) -> NSArray? {
- guard let key else { return nil }
- return self[key] as? NSArray
- }
- }
- public extension UIColor {
- @objc(ln_colorWithHexString:)
- static func ln_color(hexString: String) -> UIColor? {
- var normalized = hexString.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
- if normalized.hasPrefix("#") {
- normalized.removeFirst()
- } else if normalized.hasPrefix("0X") {
- normalized.removeFirst(2)
- }
- guard [3, 4, 6, 8].contains(normalized.count) else { return nil }
- func hexValue(_ text: String) -> CGFloat? {
- UInt64(text, radix: 16).map { CGFloat($0) / 255.0 }
- }
- let r: CGFloat
- let g: CGFloat
- let b: CGFloat
- let a: CGFloat
- switch normalized.count {
- case 3, 4:
- guard
- let rr = hexValue(String(normalized[normalized.startIndex])),
- let gg = hexValue(String(normalized[normalized.index(normalized.startIndex, offsetBy: 1)])),
- let bb = hexValue(String(normalized[normalized.index(normalized.startIndex, offsetBy: 2)]))
- else { return nil }
- r = rr
- g = gg
- b = bb
- if normalized.count == 4 {
- let index = normalized.index(normalized.startIndex, offsetBy: 3)
- guard let aa = hexValue(String(normalized[index])) else { return nil }
- a = aa
- } else {
- a = 1
- }
- case 6, 8:
- let start = normalized.startIndex
- let r0 = normalized[start...normalized.index(start, offsetBy: 1)]
- let g0 = normalized[normalized.index(start, offsetBy: 2)...normalized.index(start, offsetBy: 3)]
- let b0 = normalized[normalized.index(start, offsetBy: 4)...normalized.index(start, offsetBy: 5)]
- guard
- let rr = hexValue(String(r0)),
- let gg = hexValue(String(g0)),
- let bb = hexValue(String(b0))
- else { return nil }
- r = rr
- g = gg
- b = bb
- if normalized.count == 8 {
- let a0 = normalized[normalized.index(start, offsetBy: 6)...normalized.index(start, offsetBy: 7)]
- guard let aa = hexValue(String(a0)) else { return nil }
- a = aa
- } else {
- a = 1
- }
- default:
- return nil
- }
- return UIColor(red: r, green: g, blue: b, alpha: a)
- }
- }
- private final class LNGestureBlockTarget: NSObject {
- private let block: (Any) -> Void
- init(block: @escaping (Any) -> Void) {
- self.block = block
- }
- @objc func invoke(_ sender: Any) {
- block(sender)
- }
- }
- private var lnGestureTargetKey: UInt8 = 0
- public extension UIGestureRecognizer {
- @objc(ln_initWithActionBlock:)
- convenience init(lnActionBlock: @escaping (Any) -> Void) {
- self.init()
- ln_addActionBlock(lnActionBlock)
- }
- @objc(ln_addActionBlock:)
- func ln_addActionBlock(_ block: @escaping (Any) -> Void) {
- let target = LNGestureBlockTarget(block: block)
- addTarget(target, action: #selector(LNGestureBlockTarget.invoke(_:)))
- var targets = objc_getAssociatedObject(self, &lnGestureTargetKey) as? [LNGestureBlockTarget] ?? []
- targets.append(target)
- objc_setAssociatedObject(self, &lnGestureTargetKey, targets, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- }
- @objc(ln_removeAllActionBlocks)
- func ln_removeAllActionBlocks() {
- let targets = objc_getAssociatedObject(self, &lnGestureTargetKey) as? [LNGestureBlockTarget] ?? []
- for target in targets {
- removeTarget(target, action: #selector(LNGestureBlockTarget.invoke(_:)))
- }
- objc_setAssociatedObject(self, &lnGestureTargetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- }
- }
- private enum LNNotificationQueueStore {
- static let lock = NSRecursiveLock()
- static var store: [String: OperationQueue] = [:]
- }
- public extension Notification.Name {
- var ln_notificationOperationQueue: OperationQueue {
- get {
- LNNotificationQueueStore.lock.lock()
- defer { LNNotificationQueueStore.lock.unlock() }
- if let queue = LNNotificationQueueStore.store[rawValue] {
- return queue
- }
- let queue = OperationQueue()
- queue.maxConcurrentOperationCount = 1
- LNNotificationQueueStore.store[rawValue] = queue
- return queue
- }
- nonmutating set {
- LNNotificationQueueStore.lock.lock()
- LNNotificationQueueStore.store[rawValue] = newValue
- LNNotificationQueueStore.lock.unlock()
- }
- }
- }
- private final class LNWeakNotificationBox: NSObject {
- weak var observer: NSObjectProtocol?
- weak var center: NotificationCenter?
- var token: NSObjectProtocol?
- init(observer: NSObjectProtocol, center: NotificationCenter) {
- self.observer = observer
- self.center = center
- }
- }
- public extension NotificationCenter {
- @objc(ln_addSafeObserver:selector:name:object:)
- func ln_addSafeObserver(_ observer: NSObject, selector: Selector, name: Notification.Name?, object: Any?) {
- if #available(iOS 9.0, *) {
- addObserver(observer, selector: selector, name: name, object: object)
- return
- }
- let queue = name?.ln_notificationOperationQueue
- let box = LNWeakNotificationBox(observer: observer, center: self)
- box.token = addObserver(forName: name, object: object, queue: queue) { [weak box] note in
- guard let box, let target = box.observer else {
- if let token = box?.token {
- box?.center?.removeObserver(token)
- }
- return
- }
- _ = (target as AnyObject).perform(selector, with: note)
- }
- }
- @objc(ln_addSafeObserver:selector:name:object:queue:)
- func ln_addSafeObserver(_ observer: NSObject, selector: Selector, name: Notification.Name?, object: Any?, queue: OperationQueue) {
- if let name {
- name.ln_notificationOperationQueue = queue
- }
- ln_addSafeObserver(observer, selector: selector, name: name, object: object)
- }
- func ln_addWeakObserver(_ weakObserver: NSObject, name: Notification.Name?, using block: @escaping (Notification, NSObject) -> Void) {
- let box = LNWeakNotificationBox(observer: weakObserver, center: self)
- box.token = addObserver(forName: name, object: nil, queue: nil) { [weak box] note in
- guard let box, let target = box.observer as? NSObject else {
- if let token = box?.token {
- box?.center?.removeObserver(token)
- }
- return
- }
- block(note, target)
- }
- }
- }
|