快速替代drawInRect的方法

4
什么是在高帧率下呈现和缩放图像的最快方法?
我使用drawRect绘制(并缩放)NSBitmapImageRep以达到约30FPS,但它占用大量CPU。
以下是每秒设置NSBitmapImageRep像素的示例代码:
NSInteger x, y;
unsigned char *imgptr = nsBitmapImageRepObj.bitmapData;
unsigned char *ptr;
NSInteger rowBytes = nsBitmapImageRepObj.bytesPerRow;

for (y = 0; y < nsInputFrameRect.size.height; y++) {
    ptr = imgptr + (y * rowBytes);
    for (x = 0; x < nsInputFrameRect.size.width; x++) {
        *ptr++ = 1; // R
        *ptr++ = 2; // G
        *ptr++ = 3; // B
    }

}
[self setNeedsDisplay:YES];

以30FPS的帧率进行绘制:

- (void)drawRect:(NSRect)pRect {
    [NSGraphicsContext saveGraphicsState];    
    [nsBitmapImageRepObj drawInRect:pRect];
    [NSGraphicsContext restoreGraphicsState];    
} // end drawRect

NSBitmapImageRep的drawInRect使用了大量的CPU。在高帧率下,缩放和绘制图像的最快方式是什么?源图像应该是一张可以直接设置像素的图像,例如通过获取位图数据指针。
如果将NSBitmapImageRep转换为CIImage并使用[ciImage drawInRect]进行绘制,则速度约快10%。如果绘制到NSOpenGLView而不是NSView,则速度再次提高约10%,但仍然需要大量CPU时间来进行缩放/drawInRect操作。

你是否在尝试根据公式为每个像素着色?这种操作最适合GPU。根据你要做的具体内容,你的解决方案可能从OpenGL着色器到CoreImage甚至只是Quartz不等。 - Brian Nickel
这是用于简单卡拉OK图形的,但它并不是公式化的:我更新一个低分辨率的NSBitmapImageRep图像,然后将该小图像缩放到大约30FPS的屏幕大小。在小的NSBitmapImageRep中设置像素非常快,但是使用drawInRect将图像定期放大到屏幕大小非常慢。将CIImage放大是否更快?或者当可以逐像素设置源图像时,是否有更快的缩放图像的方法? - CocoaMug
1个回答

1
首先,您应该删除保存和恢复 NSGraphicsContext 的操作,因为 - [NSImageRep drawInRect:] 方法自己完成这个过程。请参见“Cocoa绘图图形上下文”:
Important: Saving and restoring the current graphics state is a relatively expensive operation that should done as little as possible.

下一个要点是您像素数据的结构:每个样本8位,每个像素3个样本,这意味着每个像素24位。这非常不友好硬件(GPU?),因为32位值比24位值更快处理。有一种简单的方法可以使像素处理更加友好(更快):使用4个字节而不是3个字节:向每个像素添加一个填充字节,这不会损害图像!这个填充字节不是图像的一部分,而只是存储像素的存储器的一部分,并且在任何绘图操作中都不使用。它只是用来加速像素操作。因此,在NSBitImageRep的描述中使用此内容:
bitsPerSample:8
samplesPerPixel:3
hasAlpha:NO
isPlanar:NO    // must be meshed
bytesPerRow:0  // let the system do it
bitsPerPixel:32   // <-- important !!

因此,您的代码必须进行修改:
    *ptr++ = 3; // B
    *ptr++ = 123; // new!! value is unimportant

顺便提一下:如果你读取一个JPEG图像到NSBitmapImageRep中,则这些图像表示始终具有3字节像素加上一个填充字节。为什么会浪费存储空间?这样做有很好的理由!


谢谢,我已经删除了上下文保存并转移到32bpp。这很有道理,但在我的情况下,32bpp对性能没有明显的影响。从200x200缩放到400x400大约需要7%的CPU,但是从缩放到大约1600x1600需要34%的CPU,这似乎全部在drawInRect()调用中。也许NSBitmapImageRep drawInRect对我来说没有使用GPU。使用CIImage更合适以确保由GPU处理吗? - CocoaMug

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接