总述
从总体上来讲它是容纳一个纹理的容器。这个类写得太别的散,让人一眼看不出什么主旨要义来,可能是因为作者像让这个类处理太多东西了。它接收一个纹理和这个纹理的大小,然后经过一番处理然后生成一个什么东西,虽然主要就是个图片而已。此外,它还涉及到2个重要的操作,一个是内存上的控制,一个是并发上的控制。
内部构成
GLuint framebuffer:这个就是缓冲区的实质,它就是缓冲区。
CVPixelBufferRef renderTarget:它就是渲染的目标。
CVOpenGLESTextureRef renderTexture:这是需要被渲染的纹理。
NSUInteger readLockCount: 这个应该是读写锁,好像是说只要这个缓冲区被读取它就不能被更改,所以上个锁。
NSUInteger framebufferReferenceCount:这就是那个引用计数,每个对象的引用计数。这个类似OC对象的引用计数。
BOOL referenceCountingDisabled:这个是引用计数的开关。
-(void)generateFramebuffer:用来生成一个缓冲区,这里面的过程就比较复杂了。
-(void)generateTexture:用来生成一个纹理,内部使用的。
-(void)destroyFramebuffer:用于销毁帧缓冲区。
这里还有两个C语言的回调dataProviderReleaseCallback和dataProviderUnlockCallback。
generateTexture
从这个名字就可以看出来它是用来生成纹理的,具体是怎么生成纹理的,全是都用OpenGL ES 2.0来生成的。这里面首先是要选择一个待激活的纹理单元,纹理单元的数量至少是8个。然后是生成纹理的名字,用一个数组来存储存储生成的名字,有几个名字数组的元素个数就是几个。接下来是要把名字和激活的纹理单元绑定,我想这个过程就是给纹理单元命名吧。最后是把纹理以某种方式映射成像素。反正全都是OpenGL ES的操作。
generateFramebuffer
这个就是生成帧缓冲区,整个过程都是同步执行的。在OpenGL ES中生成帧缓冲区和生成纹理有点相似。首先指定一下上下文,然后就是帧缓冲区的专属部分了。首先要生成帧缓冲区的名字,一个帧缓冲区配一个名字。然后通过名字把对应的帧缓冲区绑定到某目标上去。须知与设备有关的一个属性是在iOS 5.0及以上所有的帧缓冲区都是备份在纹理的缓存中,所以该方法的逻辑进行到这都需要进行一个判断,如果是支持备份的话,是一种处理,否则是另一种处理方式。
如果是支持这种特性的,首先要获取纹理的缓存,这个缓存是CVOpenGLESTextureCacheRef类型的。然后用一个可变字典去获取属性,然后把这些属性赋值给一个不可变字典。在这个过程中,有一个error对象,这就好像是一个异常对象一样,从该对象中可以获取错误信息。前半过程已经处理完了,这一段就是获取属性,获取完了以后就释放临时的变量。接下来是纯的OpenGL ES的处理过程,简单就是获取当前的纹理,并从当前的纹理获取帧缓冲区。
如果不支持特性,此时就代表该设备不支持从纹理缓存获取帧缓冲区。那就新生成1个纹理,然后通过这个纹理获取帧缓冲区。
这两个分支都走完以后,就获取缓冲区的状态,最后是绑定纹理,但是为什么最后要绑定纹理呢?
destroyFramebuffer
很显然嘛,这就是用来释放帧缓冲区用的。它是同步处理的,处理的过程是这样的。首先获取上下文,然后如果缓冲区不空的话就释放帧缓冲区。接下来的处理过程也分两种情况,就是如果设备支持纹理缓存和不支持纹理缓存的情况,另外还有一个判断条件是是否允许丢失帧缓冲区。
如果支持并且还不支持丢失帧缓冲区的话,那就释放渲染目标和渲染的纹理。
否则,直接就删除纹理即可。
activateFramebuffer
激活帧缓冲区用的,但是这里面的实现极其简单,就两句话。它首先是要把帧缓冲区绑定到指定目标上面,然后是要把这个帧缓冲区转换成一个size大小的图片纹理,由此可见帧缓冲区最终要被转换成一个size的,而且转换的步骤就在此处啊,不过这个size具体是多大,它决定于类外部的设置。
帧缓冲区的引用计数机制
这个就是参考了OC对象的引用计数机制,概述一下就是这个缓冲区被引用的话,引用计数就+1,放弃引用,引用计数就-1,减到0以后,按照oc的机制的话是释放对象,但是在GPUImage中则是把帧缓冲区归还到帧缓冲区的缓存中。
lock
它是手动增加引用计数的方法。它先检查是否禁用了引用计数,如果禁用了,就什么都不做直接返回,否则把该缓冲区的引用计数+1.
unlock
这是手动减少引用计数的方法。如果引用计数被禁用了,就直接返回。如果引用计数没有被减的时候就已经是0,则会触发断言,而不是直接归还到缓存中去。
clearAllLocks
这个没啥好说的,就是清空引用计数,但是它没有归还帧缓冲区。
disableReferenceCounting
禁用引用计数。
enableReferenceCounting
激活引用计数。
图像捕捉
就是说从帧缓冲区能够计算出图像。
dataProviderReleaseCallback
释放数据,一个C语言的语法,看上去有点类似于内联函数。这是一个回调,传进来的数据就是希望被释放的数据。
dataProviderUnlockCallback
这也是个回调,它用于把回调进来的数据降低它的引用计数。因为这个帧缓冲区总是要绑定到一个目标上的,你不能因为这个帧缓冲区不要了也不要这个与之绑定的目标了,于是要先恢复它的渲染目标。然后引用计数减一,然后,因为它不能在被用于获取图像了嘛,所以你需要把它从能用于获取图片的帧缓冲列表中删除。
newCGImageFromFramebufferContents
它是能够从帧缓冲区中获取一个新的cg图像。整个过程也是同步进行的。首先是要获取上下文。接下来是获取生成图片所需要的总的字节数,这个字节数是图像的宽度×高度×4,而这个宽度和高度是通过属性外部设置的size。
如果设备是支持纹理缓存的话,它要从渲染目标中获取每行的像素的字节数,再除以4,得到每行中的单元数,至于什么单元,我就不知道了,然后宽度×高度×4,知道尺寸以后,要先把引用计数+1,然后读取。获取渲染目标的地址,通过数据获取数据的提供者,这些提供者是什么呀?然后把帧缓冲区添加入到可以获取图片的激活的图像链表中。
如果设备不支持纹理缓存的话,则直接激活帧缓冲区,然后通过属性设置的size来获取图片的像素数和字节数,进而得到数据提供者,然后帧缓冲区降低引用计数。
接下来,是从设备上获取RGB颜色空间。
如果设备支持纹理缓存的话,通过上面的数据提供器还有RGB颜色空间并且使用渲染目标就可以得到cg图像。
如果设备不支持纹理缓存的话,就通过属性size设置的数据来获取cg图像。
最后得到结果,就是一个cg图像。
restoreRenderTarget
恢复渲染的目标。这个实现很简单,也是两行代码,就是在读完以后降低它的引用计数,并且释放渲染目标。
数据读取机制
就是在读取数据,控制引用计数。就是在读的时候引用计数+1,不读的时候引用计数-1.
lockForReading
只有设备支持纹理缓存的时候,才进行此操作,这里面涉及到了另外一个读计数,一开始的时候计数是0就先锁定像素缓冲区的基址,然后读计数+1.
unlockAfterReading
它也是只针对支持纹理缓存的设备而言的,读计数减1,到了0以后就解锁像素缓冲区的基址。
bytesPerRow
它返回图像纹理每行的字节数。如果设备支持纹理,则只通过渲染目标就可以得到每行的字节数。如果不是iPhone或者模拟器或者不支持纹理缓存,则返回通过属性设置的size的宽度×4.
byteBuffer
就是通过渲染目标获取像素缓冲区的基址,之前要锁定来读取,之后要解锁。
pixelBuffer
其实就是返回渲染目标,而对于非iPhone或者模拟器平台则直接返回NULL。
texture
用于持久化输入的纹理。
网友评论