在上一篇文章中介紹了GPUImage框架中的核心載體GPUImageFrameBuffer
,在接下來的文章中,我們將介紹如何使用這個載體實(shí)現(xiàn)渲染和傳遞。本文將重點(diǎn)介紹GPUImage中的一個非常重要的基類GPUImageOutput
和一個協(xié)議GPUImageInput
。基本上所有重要的GPUImage處理類都是GPUImageOutput
的子類,它實(shí)現(xiàn)了一個輸出的基本功能。
同樣的,基本上所有的GPUImage處理類也都遵循GPUImageInput
協(xié)議。它定義了一個能夠接收frameBuffer的接收者所必須實(shí)現(xiàn)的基本功能。主要包括:
*接收上一個GPUImageOutput
的相關(guān)信息;
*接收并處理上一個GPUImageOutput
渲染完成的通知;
GPUImageOutput
GPUImageOuput
實(shí)現(xiàn)了一些作為一個Output的基本功能,主要包括:
- 生成,管理到
GPUImageFrameBuffer
; - Target的添加以及管理,用來生成整個FilterChain;
- 產(chǎn)出一個渲染的結(jié)果;
GPUImageFrameBuffer的管理
每個GPUImageOutput都會包含一個自己的GPUImageFrameBuffer對象;可以通過outputFrameBuffer
方法獲得。這個FrameBuffer對象就是當(dāng)前Output渲染的對象。
這個FrameBuffer對象不是一直存在的,而是當(dāng)這個Output需要進(jìn)行渲染的時候,才會從GPUImageFrameBufferCache
中取一個。因此,并不是隨時都能夠獲得一個GPUImageFrameBuffer對象的。一般情況下,當(dāng)渲染完畢并且通知了FilterChain中的下一個target之后,就會被remove掉,并且歸還給GPUImageFrameBufferCache
以供重用。
與FrameBuffer相關(guān)的方法有:
- (void)setInputFramebufferForTarget:(id<GPUImageInput>)target atIndex:(NSInteger)inputTextureIndex;
這個方法的調(diào)用發(fā)生在當(dāng)前output渲染完畢后,需要通知下一個receiver可以開始渲染的時候,把當(dāng)前Output的FrameBuffer傳遞給下一個Input,讓它可以使用這個FrameBuffer的結(jié)果進(jìn)行渲染。
- (GPUImageFramebuffer *)framebufferForOutput;
這個方法可以獲得當(dāng)前正在渲染的frameBuffer,但是這個方法更大的用處是給子類提供一個覆蓋的接口。子類可以通過覆蓋這個方法,來提供輸出的frameBuffer。因?yàn)橛幸恍┒鄠€pass的濾鏡可能會有多個FrameBuffer。
- (void)removeOutputFramebuffer;
這個方法用來移除當(dāng)前渲染的frameBuffer,同樣,這個方法更大的用處是給子類提供一個移除當(dāng)前FrameBuffer的機(jī)會。
Target的管理
GPUImageOutput
既然作為一個輸出,那么自然就應(yīng)該有對應(yīng)的接受者來接受這個FrameBuffer并且使用這個Output處理的結(jié)果進(jìn)行渲染。這些接受者我們都將它稱之為target。每個Target都是一個實(shí)現(xiàn)了GPUImageInput
協(xié)議的對象。這些對象可以接收FrameBuffer,處理當(dāng)前Output渲染完畢的通知等等。GPUImageInput
會在接下來的解析中詳細(xì)介紹。
與Target管理相關(guān)的方法有:
- (NSArray*)targets;
這個方法可以獲取到當(dāng)前Output所有的target。每個Output都可以添加多個target,當(dāng)這個Output渲染完成之后,每個target都會收到通知。
- (void)addTarget:(id<GPUImageInput>)newTarget;
- (void)addTarget:(id<GPUImageInput>)newTarget atTextureLocation:(NSInteger)textureLocation;
這兩個addTarget方法的作用都是將下一個實(shí)現(xiàn)了GPUImageInput
協(xié)議的對象添加到FilterChain當(dāng)中來。在一個GPUImageInput
被添加到FilterChain之后,它做的主要事情包括:將當(dāng)前Output的outputFrameBuffer
作為input傳遞給這個GPUImageInput
對象;設(shè)置這個outputFrameBuffer
所在的TextureLocation。TextureLocation的作用是:有些可以同時接受多個Input的對象,它要將不同的frameBuffer的texture綁定到不同的位置上。因?yàn)槊總€GL_TEXTURE
同時只能接受一個texture的綁定。
[self setInputFramebufferForTarget:newTarget atIndex:textureLocation];
[targets addObject:newTarget];
[targetTextureIndices addObject:[NSNumber numberWithInteger:textureLocation]];
移除Targets:
- (void)removeTarget:(id<GPUImageInput>)targetToRemove;
- (void)removeAllTargets;
這兩個方法的作用是將某一個或者所有的target都移出FilterChain。當(dāng)一個target被移出FilterChain之后,它將不會再收到任何當(dāng)前Output渲染完成的通知。
獲取當(dāng)前FrameBuffer的處理結(jié)果
GPUImageOutput
提供了一些從當(dāng)前Output獲得處理結(jié)果的方法,讓使用者可以簡單的獲得處理的結(jié)果:
- (CGImageRef)newCGImageFromCurrentlyProcessedOutput;
- (CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter;
- (UIImage *)imageFromCurrentFramebuffer;
- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation;
- (UIImage *)imageByFilteringImage:(UIImage *)imageToFilter;
- (CGImageRef)newCGImageByFilteringImage:(UIImage *)imageToFilter;
其中最核心的方法是newCGImageFromCurrentlyProcessedOutput
,基本上所有的方法最終都調(diào)用了這個方法。但是GPUImageOutput
并沒有為這個方法提供默認(rèn)的實(shí)現(xiàn),而是提供了一個方法定義。具體的實(shí)現(xiàn)在它的兩個重要的子類GPUImageFilter
和GPUImageFilterGroup
中。而實(shí)際上最終調(diào)用的方法都在GPUImageFilter
中實(shí)現(xiàn)了,GPUImageFilterGroup
的實(shí)現(xiàn)實(shí)際上是調(diào)用了它的terminalFilter
中的實(shí)現(xiàn)。
在獲取一個FilterChain中的一個GPUImageOutput
的處理結(jié)果時,有一個非常重要的方法需要調(diào)用:
- (void)useNextFrameForImageCapture;
在我們上一篇介紹GPUImageFrameBuffer
的文章中,我們說到了所有的FrameBuffer都是有引用計數(shù)的,當(dāng)一個FrameBuffer的引用計數(shù)為零的時候,它會被歸還到FrameBufferCache中。當(dāng)一個GPUImageOutput
處于一個FilterChain中的時候,他渲染完成并且通知下一個input,當(dāng)下一個input也渲染完成之后,這個FrameBuffer的引用計數(shù)就會為零,因此也會被釋放掉。這個時候如果調(diào)用newCGImageFromCurrentlyProcessedOutput
方法的話,就會多次釋放一個FrameBuffer導(dǎo)致Crash,或者獲取不到這個FrameBuffer。
而useNextFrameForImageCapture
方法的左右就是:在渲染的時候,再調(diào)用一次GPUImageFrameBuffer
的lock
方法,強(qiáng)行將引用計數(shù)+1.這樣這個FrameBuffer就會一直存在于這個Output中,可以用來進(jìn)行渲染結(jié)果的獲取。
GPUImageInput
GPUImageInput
協(xié)議是為了保證每一個接收GPUImageOutput
輸出的對象都能夠正確地處理對應(yīng)的輸入的。它的功能主要包括:
- 接收
GPUmageOutput
的輸出信息; - 處理
GPUImageOutput
渲染完成的通知;
接收GPUImageOutput的輸出信息
根據(jù)之前介紹的GPUImageOutput
,它添加的每一個Target都必須實(shí)現(xiàn)了GPUImageInput
協(xié)議。因此GPUImageInput
協(xié)議保證了所有實(shí)現(xiàn)它的對象都能夠接收output的輸出。主要輸出信息包括:
- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
- (NSInteger)nextAvailableTextureIndex;
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
GPUImageInput
可以接收的信息包括上一個Output輸出的frameBuffer,frameBuffer的size以及rotation。同樣的這些textureIndex
都是為了提供個需要多個input的Filter準(zhǔn)備的。
處理GPUImageOutput渲染完成的通知
GPUImageOutput
在渲染完成之后,會通知它所有的targets,因此,GPUImageInput
需要實(shí)現(xiàn)下面這個方法來接收這個通知,并且開始自己的渲染:
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
其中,參數(shù)frameTime
主要是給視頻處理的時候使用的,當(dāng)多個輸入都是視頻的時候,可以使用frameTime來確保多個輸入是同步的;
本文主要介紹了GPUImage中最重要的基類和協(xié)議GPUImageOutput
和GPUImageInput
,它們具體的實(shí)現(xiàn)都在GPUImageFilter
以及GPUImageFilterGroup
中,我們會在接下來的解析中具體看看他們的實(shí)現(xiàn)。