ARKit 如何給SCNNode貼Gif圖片

最近在研究如何在SCNNode上加載Gif圖片璧帝,總結出兩種解決方案叔锐。
首先介紹下SCNMaterial,它是SCNNode的材質(zhì)屬性紧帕,可以通過它給Node添加各種皮膚材質(zhì)盔然,根據(jù)官方文檔,SCNMaterial的contents可以用UIColor是嗜、UIImage愈案、CALayer、NSURL等等鹅搪,真是無敵了站绪,雖然UIView沒有提及,但是我自己試驗之后也是可以的丽柿,不過加載Gif會堵塞UI線程恢准。

Specifies the receiver's contents. This can be a color (NSColor, UIColor, CGColorRef), 
an image (NSImage, UIImage, CGImageRef), a layer (CALayer), a path (NSString or NSURL), 
a SpriteKit scene (SKScene), a texture (SKTexture, id<MTLTexture> or GLKTextureInfo), 
or a floating value between 0 and 1 (NSNumber) for metalness and roughness properties.
png
一 通過將Gif圖片轉成MP4文件

需要先將Gif圖片轉成MP4文件,存在緩存中甫题,通過AVPlayer即可加載馁筐,轉mp4還是有些耗時,建議后臺進行預處理坠非。

//此處貼出關鍵代碼
let fileData:NSData = model.getFileData()
let tempUrl = URL(fileURLWithPath:NSTemporaryDirectory()).appendingPathComponent("temp.mp4")
GIF2MP4(data: fileData as Data)?.convertAndExport(to: tempUrl, completion: {
    let playerItem = AVPlayerItem.init(url: tempUrl)
    let player = AVPlayer.init(playerItem: playerItem)
    player.play()
              
    material.diffuse.contents = player
    })
}

func convertAndExport(to url :URL , completion: @escaping () -> Void ) {
        outputURL = url
        prepare()
        
        var index = 0
        var delay = 0.0 - gif.frameDurations[0]
        let queue = DispatchQueue(label: "mediaInputQueue")
        videoWriterInput.requestMediaDataWhenReady(on: queue) {
            var isFinished = true
            
            while index < self.gif.frames.count {
                if self.videoWriterInput.isReadyForMoreMediaData == false {
                    isFinished = false
                    break
                }
                
                if let cgImage = self.gif.getFrame(at: index) {
                    let frameDuration = self.gif.frameDurations[index]
                    delay += Double(frameDuration)
                    let presentationTime = CMTime(seconds: delay, preferredTimescale: 600)
                    let result = self.addImage(image: UIImage(cgImage: cgImage), withPresentationTime: presentationTime)
                    if result == false {
                        fatalError("addImage() failed")
                    } else {
                        index += 1
                    }
                }
            }
            
            if isFinished {
                self.videoWriterInput.markAsFinished()
                self.videoWriter.finishWriting() {
                    DispatchQueue.main.async {
                        completion()
                    }
                }
            } else {
                // Fall through. The closure will be called again when the writer is ready.
            }
        }
    }

二 通過關鍵幀動畫來實現(xiàn)
//獲取Gif圖片數(shù)據(jù)
let fileData = model.getFileData()
let animation : CAKeyframeAnimation = createGIFAnimationYY(data: fileData)

//通過CALayer給material賦值                
let layer = CALayer()
layer.bounds = CGRect(x: 0, y: 0, width: imageW, height: imageH)
layer.add(animation, forKey: "contents")

//直接將CALayer賦值給material敏沉,Gif只會顯示右下角1/4                
let tempView = UIView.init(frame: CGRect(x: 0, y: 0, width: imageW, height: imageH))
tempView.layer.bounds = CGRect(x: -imageW/2, y: -imageH/2, width: imageW, height: imageH)
tempView.layer.addSublayer(layer)
material.diffuse.contents = tempView.layer

轉成關鍵幀動畫,由于有些Gif很大炎码,幀數(shù)很多盟迟,此處我做了一些內(nèi)存優(yōu)化,極端情況內(nèi)存占用優(yōu)化至之前的1/10辅肾,這種方法相比于上一種加載速度會更快一點队萤,但內(nèi)存占用稍大轮锥。

func createGIFAnimationYY(data:NSData) -> CAKeyframeAnimation {
    
    let image = YYImage.init(data: data as Data)
    let bytes = Int.init(bitPattern: (image?.animatedImageBytesPerFrame())!)
    let nFrame = Int.init(bitPattern: (image?.animatedImageFrameCount())!)
    let bufferSize = Float(bytes*nFrame)
    let total = getDeviceMemoryTotal();
    let free = getDeviceFreeMemory();
    print("start: bufferSize:",bufferSize," totalMem: ",total," free: ",free)
    var maxSize = min(total*0.2, free!*0.6)
    maxSize = max(maxSize, Float(BUFFER_SIZE))
    if maxSize > bufferSize {
        maxSize = bufferSize
    }
    var maxBufferCount = Int(maxSize / Float(bytes))
    if maxBufferCount < 1 {
        maxBufferCount = 1
    } else if maxBufferCount > 256 {
        maxBufferCount = 256
    }
    //實際幀數(shù)跟優(yōu)化幀數(shù)比值
    let ratio = Float(nFrame)/Float(maxBufferCount)
    
    // Total loop time
    var time : Float = 0
    
    // Arrays
    var framesArray = [AnyObject]()
    var tempTimesArray = [NSNumber]()
    
    for i in 0..<maxBufferCount {
        let curFrame = image?.animatedImageFrame(at: UInt(Float(i)*ratio))
        framesArray.append(curFrame!.cgImage!)
        var frameDuration = image?.animatedImageDuration(at: UInt(Float(i)*ratio))
        if frameDuration! - 0.011 < 0 {
            frameDuration = 0.100;
        }
        tempTimesArray.append(NSNumber(value: frameDuration!))
        print(frameDuration)
        time = time + Float(frameDuration!)
    }
    print("End: ",time)
    
    var timesArray = [NSNumber]()
    var base : Float = 0
    for duration in tempTimesArray {
        timesArray.append(NSNumber(value: base))
        base = base.advanced(by: duration.floatValue / time)
    }
    
    timesArray.append(NSNumber(value: 1.0))
    
    // Create animation
    let animation = CAKeyframeAnimation(keyPath: "contents")
    
    animation.beginTime = AVCoreAnimationBeginTimeAtZero
    animation.duration = CFTimeInterval(time)
    animation.repeatCount = Float.greatestFiniteMagnitude;
    animation.isRemovedOnCompletion = false
    animation.fillMode = kCAFillModeForwards
    animation.values = framesArray
    animation.keyTimes = timesArray
    animation.calculationMode = kCAAnimationDiscrete
    
    return animation;
}
效果圖
demo.gif

原文鏈接:http://www.reibang.com/p/f5d13540c08d

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矫钓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子舍杜,更是在濱河造成了極大的恐慌新娜,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件既绩,死亡現(xiàn)場離奇詭異概龄,居然都是意外死亡,警方通過查閱死者的電腦和手機饲握,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門私杜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚕键,“玉大人,你說我怎么就攤上這事衰粹÷喙猓” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵铝耻,是天一觀的道長誊爹。 經(jīng)常有香客問我,道長瓢捉,這世上最難降的妖魔是什么频丘? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮泡态,結果婚禮上搂漠,老公的妹妹穿的比我還像新娘。我一直安慰自己某弦,他們只是感情好状答,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刀崖,像睡著了一般惊科。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亮钦,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天馆截,我揣著相機與錄音,去河邊找鬼蜂莉。 笑死蜡娶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的映穗。 我是一名探鬼主播窖张,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚁滋!你這毒婦竟也來了宿接?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辕录,失蹤者是張志新(化名)和其女友劉穎睦霎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體走诞,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡副女,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚣旱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碑幅。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡戴陡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沟涨,到底是詐尸還是另有隱情猜欺,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布拷窜,位于F島的核電站开皿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏篮昧。R本人自食惡果不足惜赋荆,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懊昨。 院中可真熱鬧窄潭,春花似錦、人聲如沸酵颁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躏惋。三九已至幽污,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間簿姨,已是汗流浹背距误。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扁位,地道東北人准潭。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像域仇,于是被迫代替她去往敵國和親刑然。 傳聞我的和親對象是個殘疾皇子稻扬,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫髓迎、插件畸陡、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,102評論 4 62
  • 江湖自在人心螟蒸,你心里有江湖,那就是江湖匠抗;如果沒有灭翔,可能是天堂、可能是地獄蝠检、或是庸庸碌碌、匆匆忙忙挚瘟、平平淡淡了...
    夜已空閱讀 488評論 4 9
  • 過了幾天,我們?nèi)乙匕职值膯挝涣恕?那一天是我叔叔要送我們焰檩,叔叔趕了一個馬車憔涉,那是一批棗紅色的漂亮的不停的走來走...
    姚君心理咨詢師閱讀 304評論 0 1
  • 原文:子曰:“禘,自既灌而往者析苫,吾不欲觀之矣兜叨。” 孔子說:“(他們的)天子祭祀儀式衩侥,從祭酒開始后所有的一切禮儀国旷,我...
    哈皮波閱讀 1,097評論 0 0