如何直接更新像素 – 使用CGImage和直接CGDataProvider

实际问题

几个答案将解决我的问题:

  1. 我可以像CGContextDrawImage一样强制CGImage从直接数据提供者(使用CGDataProviderCreateDirect创build)重新加载它的数据吗? 或者有其他的方法可以设置self.layer.contents来做到这一点?
  2. 是否有一个CGContextconfiguration,或者我可以使用CGContextDrawImage一致呈现1024×768图像至less30 fps的技巧。
  3. 有没有人能够成功地使用CVOpenGLESTextureCacheCreateTextureFromImage实时缓冲区更新与他们自己的纹理数据? 我认为我最大的问题是创build一个CVImageBuffer因为我从Apples的纹理文档复制了其他属性。 如果任何人有更多的信息,这将是很棒的。
  4. 任何其他的指导方针,我怎么可以从内存中获取图像在30 fps的屏幕上。

背景(批次):

我正在做一个项目,我需要实时修改NPOT图像数据的像素(最低30帧/秒),并在iOS屏幕上绘制。

我的第一个想法是使用OpenGL与glTexSubimage2D进行更新,不幸的是,结果是非常慢(在iPad上6 fps),因为驱动程序将每帧的RGB数据转换为BGR。 所以发送它在BGR你说,我也是这样,但由于某种原因,你不能用GL_BGR去调用GL_BGR去图。 我知道一些缓慢是因为它是不是2图像数据的力量,但我的要求规定。

更多的阅读导致我CVOpenGLESTextureCacheCreateTextureFromImage但所有的例子是它使用直接相机input获取CVImageBufferRef我试图使用文档(没有官方,但只是头注释),使我自己的CVImageBuffer形成我的图像数据,但它不会使用这个(在debugging器中没有错误只是一个空的纹理),这使得我认为苹果公司专门build立了这个处理实时相机数据,除了IDK之外,还没有经过多lesstesting。

无论如何,放弃我的尊严,通过倾倒OpenGL,并将我的想法转换到CoreGraphics我被引导到这个问题最快的方式来绘制iPhone上的屏幕缓冲区 ,build议使用CGImage支持的CGDataProviderCreateDirect ,它允许您返回一个指向图像数据当CGImage需要它时,真棒吧? 那么它似乎不像广告一样。 如果我使用CGContextDrawImage那么一切正常。 我可以修改像素缓冲区,每一个绘图,它会像我们的数据提供程序那样请求图像数据,调用CGDataProviderDirectCallbacks的方法( Note:它们似乎有一个内置优化,如果它具有相同的地址如前所述)。 CGContextDrawImage是不是超快(约18 fps),即使禁用插值,从6 fps起。 苹果的文档告诉我使用self.layer.contents将比CGContextDrawImage快得多。 使用self.layer.contents适用于第一个作业,但CGImage从不要求像CGContextDrawImage那样从数据提供程序重新加载,即使在我调用[layer setNeedsDisplay] 。 在我引用的SO问题中,用户通过每一帧从数据源创build和销毁新的CGImage来解决这个问题,这是一个毫无希望的缓慢过程(是的,我尝试过了),所以真正的问题是时间了。

注意 :我已经剖析了所有这些操作,并且知道问题确实是OpenGL的glTexSubImage ,而CGContextDrawImage实际上是CoreGraphics的问题,所以没有“go profile”的回答。

演示这种技术的编辑源代码现在可以在http://github.com/narpas/image-sequence-streamingfind

感谢Brad Larson和David H的帮助(请参阅我们在评论中的完整讨论)。 原来使用OpenGL和CoreVideo的CVOpenGLESTextureCache最终成为将原始图像推送到屏幕上的最快方式(我知道CoreGraphics不可能是最快的!),在iPad 1上给我60fps的全屏1024×768图像。关于这个现在的文件,所以我会尽量帮助人们尽可能的解释:

CVOpenGLESTextureCacheCreateTextureFromImage允许您创build一个OpenGL纹理,其内存直接映射到您用来创build它的CVImageBuffer 。 这使您可以使用原始数据创buildCVPixelBuffer ,并修改从CVPixelBufferGetBaseAddress收集的数据指针。 这给你在OpenGL的即时结果,而不需要修改或重新上传实际的纹理。 只需确保在修改像素之前使用CVPixelBufferLockBaseAddress进行locking,并在完成后解锁。 请注意,目前这在iOS模拟器中不起作用,只能在我推测为来自VRAM / RAM部分的设备上,其中CPU不能直接访问VRAM。 布拉德build议使用条件编译器检查来切换原始glTexImage2D更新和使用纹理caching。

几件事情要注意(这些组合导致它不适合我):

  1. 在设备上testing
  2. 确保你CVPixelBuffer是支持与kCVPixelBufferIOSurfacePropertiesKey见例如链接 (再次感谢布拉德)。
  3. 对于OpenGL ES 2.0中的NPOT纹理数据,您必须使用GL_CLAMP_TO_EDGE
  4. 使用glBindTexture(CVOpenGLESTextureGetTarget(_cvTexture), CVOpenGLESTextureGetName(_cvTexture));绑定纹理cachingglBindTexture(CVOpenGLESTextureGetTarget(_cvTexture), CVOpenGLESTextureGetName(_cvTexture)); 不要像我这样愚蠢,并使用CVOpenGLESTextureGetTarget两个参数。
  5. 不要每一帧都重新创build纹理,只需将图像数据复制到从CVPixelBufferGetBaseAddress获取的指针中即可更新纹理。