Objective-C图形绘制速度

时间:2012-02-10 03:12:25

标签: objective-c

我想逐个像素地画到屏幕上。我有这个代码工作,但它很慢...我发现这非常奇怪,因为用javascript编写的相同类型的代码超快。足够快,我必须减慢调用此...的间隔时间。

我需要做些什么才能加快速度至少2到3次?

目标c

- (void)drawPixelAtX:(int)X atY:(int)Y
{
    [NSBezierPath fillRect:NSMakeRect(X, self.frame.size.height - Y, 1, 1)];
}


- (void)drawCharater:(int)charCode atX:(int)charX atY:(int)charY color:(NSColor*)color;
{
    if (charCode >= [self.font count])
        return;

    [color set];
    NSArray *template = [self.font objectAtIndex:charCode];
    for (int y = 0; y < 16; y++) {
        for (int x = 0; x < 8; x++) {
            if ([[template objectAtIndex:y*8+x] intValue] == 1)
                [self drawPixelAtX:(charX * 8 + x) atY:(charY * 16 + y)+1];
        }
    }
}


- (void)drawRect:(NSRect)dirtyRect
{
    [NSGraphicsContext saveGraphicsState];
    for (int x=0; x < 80; x++) {
        for (int y=0; y < 25; y++) {
            AnsiScreenChar *c = [self.screen objectAtIndex:(y*80)+x];
            [c.bgColor set];
            [NSBezierPath fillRect:NSMakeRect(x*8, self.frame.size.height - ((y+1)*16), 8, 16)];
            [self drawCharater:c.data atX:x atY:y color:c.fgColor];
        }
    }
    [NSGraphicsContext restoreGraphicsState];
}

的javascript

function updateDisplay() {
    var $this = $(this);
    var data = $this.data('ansi');

    for (var i = 0, length1 = data.screen.length; i < length1; ++i) {
        var a = data.screen[i]; // cache object
        for (var j = 0, length2 = a.length; j < length2; ++j) {
            if (a[j][4]) {
                data.ctx.save();
                data.ctx.beginPath();
                data.ctx.rect(data.fontWidth * j, data.fontHeight * i, data.fontWidth, data.fontHeight);
                data.ctx.clip();

                data.ctx.fillStyle = a[j][0];
                data.ctx.fillRect(data.fontWidth * j, data.fontHeight * i, data.fontWidth, data.fontHeight);

                data.ctx.fillStyle = a[j][1];
                drawCharater.call(this, a[j][2], [j, i], 1);
                data.ctx.restore();

                a[j][4] = false;
            }
        }
    }
}

个人资料信息

Running Time    Self        Symbol Name
12371.0ms   21.8%   12371.0     objc_msgSend
2612.0ms    4.6%    2612.0      CFNumberGetValue
2446.0ms    4.3%    2446.0      _class_getInstanceSize
1910.0ms    3.3%    1910.0      -[__NSArrayI objectAtIndex:]
1482.0ms    2.6%    1482.0      _CFExecutableLinkedOnOrAfter
1384.0ms    2.4%    1384.0      object_getIndexedIvars
1350.0ms    2.3%    1350.0      -[AnsiView drawCharater:atX:atY:color:]
1277.0ms    2.2%    1277.0      OSAtomicCompareAndSwap64Barrier$VARIANT$mp
1270.0ms    2.2%    1270.0      ripl_BltShape
1261.0ms    2.2%    1261.0      -[__NSCFNumber intValue]
1185.0ms    2.0%    1185.0      CFRelease
1047.0ms    1.8%    1047.0      CGSShmemGuardUnlock
1005.0ms    1.7%    1005.0      ripr_Rectangles

我认为CFNumberGetValue正在处理intValue电话?

3 个答案:

答案 0 :(得分:4)

调用NSBezierPath的fillRect方法来绘制每个像素是疯狂的。如果你必须一次绘制一个像素,然后绘制到你自己的缓冲区,当它准备好显示时,然后你将它发送到OS X的高度面向矢量的硬件加速的基于OpenGL的图形系统。如果你不打算利用Quartz提供的任何高级绘图原语,那么不要每帧对Quartz进行一百万次调用。如果您不想自己实施blitting,请使用SDL或Allegro。但你应该考虑是否需要逐个像素地绘制每个字母。 OS X的图形系统非常擅长合成图像,因为这是它的主要目的。顺便说一句,为什么你不能只使用普通的文本渲染方法并捆绑在自己的字体文件中?

答案 1 :(得分:3)

我要做的第一件事是分析您的代码,看看需要花费多少时间。我的猜测是花了很多时间在这里:

[[template objectAtIndex:y*8+x] intValue]

如果我的猜测是正确的,您应该可以通过将模板从NSArray切换到常规的C整数数组来获得一些速度。

修改

现在我看到了个人资料,我认为我的猜测是正确的。您是否尝试过切换到C阵列?

如果您对不同的渲染字体方法持开放态度,请参阅纹理地图集上的this article,它可能会给您一些不错的想法。

答案 2 :(得分:1)

我非常同意这种绘画方法是精神错乱。有可能有更好的方法来做到这一点。切换位图的各个像素然后调用单个绑定纹理和单个绘制是首先想到的。

假设您不会或不能更改此算法,从您的性能分析信息看起来您将花费所有时间来发送消息。我要做的第一件事就是把它全部变成一个丑陋的怪物方法而不是把它分成三个。是的,这很丑陋,但它会有效地照亮循环内的所有消息发送,只执行逻辑。

编辑:

在没有objc_messageSend ...

的情况下绘制到位图上下文
CGContextRef MyCreateBitmapContext (int pixelsWide,
                            int pixelsHigh)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

    bitmapBytesPerRow   = (pixelsWide * 4);// 1
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);

    colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);// 2
    bitmapData = calloc( bitmapByteCount );// 3
    if (bitmapData == NULL)
    {
        fprintf (stderr, "Memory not allocated!");
        return NULL;
    }
    context = CGBitmapContextCreate (bitmapData,// 4
                                    pixelsWide,
                                    pixelsHigh,
                                    8,      // bits per component
                                    bitmapBytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaPremultipliedLast);
    if (context== NULL)
    {
        free (bitmapData);// 5
        fprintf (stderr, "Context not created!");
        return NULL;
    }
    CGColorSpaceRelease( colorSpace );// 6

    return context;// 7
}

void DrawMyStuff()
{
    CGRect myBoundingBox;// 1

    myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);// 2
    myBitmapContext = MyCreateBitmapContext (400, 300);// 3
    // ********** Your drawing code here ********** // 4
    CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
    CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
    CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
    myImage = CGBitmapContextCreateImage (myBitmapContext);// 5
    CGContextDrawImage(myContext, myBoundingBox, myImage);// 6
    char *bitmapData = CGBitmapContextGetData(myBitmapContext); // 7
    CGContextRelease (myBitmapContext);// 8
    if (bitmapData) free(bitmapData); // 9
    CGImageRelease(myImage);
}

explanation of what it's all doing in the Quartz2D guide