简体   繁体   中英

Improve performance of drawRect:

I am drawing cells from a grid with a NSTimer every 0.1 Seconds. The size is about 96x64 => 6144 cells / images. If i am drawing images instead of (eg) green rectangles it is 4 times slower !

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIGraphicsPushContext(context);
    CGContextSetRGBFillColor(context, 0, 0, 0, 1);
    CGContextFillRect(context, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
    int cellSize = self.bounds.size.width / WIDTH;
    double xOffset = 0;
    for (int i = 0; i < WIDTH;i++)
    {
        for (int j = 0; j < HEIGHT;j++)
        {                
                NSNumber *currentCell = [self.state.board objectAtIndex:(i*HEIGHT)+j];
                if (currentCell.intValue == 1)
                {
                   [image1 drawAtPoint:CGPointMake(xOffset + (cellSize * i),cellSize * j )];
                }
                else if (currentCell.intValue == 0){
                   [image2 drawAtPoint:CGPointMake(xOffset + (cellSize * i),cellSize * j )];
                }
        }
    }
    UIGraphicsPopContext();
}

Any idea how to makes this faster if i want to draw png or jpg in each rectangle? The images are already scaled to an appropriate size.

  • a) Don't redraw the images/rects that are outside the view's bounds.

  • b) Don't redraw the images/rects that are outside the dirtyRect

  • c) Don't redraw the images/rects that haven't changes since the previous update.

  • d) Use a layer to prerender the images, so you don't need to render them at drawing time.

This scenario is exactly what Instruments is there for. Use it. Anyone here making a suggestion is guessing about what the bottleneck is.

That said, I'm going to guess at what the bottleneck is. You are drawing 6114 images using the CPU (confirm this by using the time profiler. Find your drawRect method, and check where the most time is spent. If it's drawInRect, then that's your problem)

If that's the case, how do we reduce its usage? An easy win would be to only redraw the images we need to draw. CALayers make this easy. Remove your drawRect method, add a sublayer to your view's layer for each image, and set the images as your layers' content properties. Instead of invalidating the view when an image needs to change, just switch the relevant layer's content property to the new image.

Another nice thing about CALayers is that they cache layer content on the GPU, meaning that the redraws that do happen will require less CPU time and won't block the rest of you app as much when they do happen.

If the overhead of that many layers is unacceptable (again, Instruments is your friend), check out CAReplicatorLayer. It's less flexible than having many CALayers, but allows a single image to be replicated many times with minimal overhead.

I tried to improve your code from performance perspective. However, check my comment about bottlenecks, too.

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    //UIGraphicsPushContext(context); //not needed UIView does it anyway

    //use [UIView backgroundColor] instead of this
    //CGContextSetRGBFillColor(context, 0, 0, 0, 1);
    //CGContextFillRect(context, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));

    int cellSize = self.bounds.size.width / WIDTH;
    double xOffset = 0;

    CGRect cellFrame = CGRectMake(0, 0, cellSize, cellSize);
    NSUinteger cellIndex = 0;

    for (int i = 0; i < WIDTH; i++) {
        cellFrame.origin.x = xOffset;

        for (int j = 0; j < HEIGHT; j++, cellIndex++) {                
            cellFrame.origin.y = 0;

            if (CGRectIntersectsRect(rect, cellFrame) {
                NSNumber *currentCell = [self.state.board objectAtIndex:cellIndex];

                if (currentCell.intValue == 1) {
                    [image1 drawInRect:cellFrame];
                }
                else if (currentCell.intValue == 0) {
                    [image2 drawInRect:cellFrame];
                }
            }

            cellFrame.origin.y += cellSize;
        }

        cellFrame.origin.x += cellSize;
    }

    //UIGraphicsPopContext(context); //not needed UIView does it anyway
}

使用CGRectIntersects检查图像的矩形是否在dirtyRect内部,以检查是否需要绘制图像。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM