从CALayer获取位图,有没有其他替代方法来渲染?

7
我有许多CALayer,这些CALayer在我的应用程序运行时动态创建,我需要能够生成这些CALayer的单个位图,稍后将其进行遮罩处理。
当我需要创建遮罩时,CALayer已经绘制到了背景中(也使用shouldRasterize = YES),并且使用renderInContext我可以得到一个位图。然而,随着CAlayer数量的增加,由renderInContext引起的暂停变得越来越长。是否有一种替代方法可以使用来渲染renderInContext,或者可以使用一种替代方法来防止应用程序被暂停冻结?
理想情况下,直接从内存/缓冲区/高速缓存中访问已绘制的像素数据而不使用OpenGL会更好,但我不确定CoreAnimation是否可以实现此功能。
谢谢,任何额外的信息都将非常有用!
2个回答

8
罗布是对的,renderInContext: 是在这里使用的正确方法。Render in context 实际上将层的像素数据渲染到上下文中。这是一个样例应用程序,可以在后台线程绘制 10,000 个图层...

该应用程序执行以下操作:

  1. 创建一个 UIView
  2. 向该视图的图层添加 10,000 个图层
  3. 当您触摸屏幕时开始渲染(即它是一个 iOS 示例应用程序)
  4. 创建一个后台线程
  5. 将 UIView 的图层渲染到上下文中(从而渲染其子层)
  6. 使用渲染上下文的内容创建 UIImage
  7. 将新图像添加到 UIImageView 中显示在屏幕上
  8. 在主线程上运行定时器,同时执行所有这些操作,以显示后台线程实际上没有阻塞主线程

以下是代码...

首先,创建一个具有许多图层的子视图:

@implementation C4WorkSpace {
    UIView *v;
    dispatch_queue_t backgroundRenderQueue;
    CFTimeInterval beginTime;
    NSTimer *timer;
    NSInteger timerCallCount;
}

-(void)setup {
    //create a view to hold a bunch of CALayers
    v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
    v.center = CGPointMake(384,512);

    //create a buch of CALayers and add them to a view
    for(int i = 0; i < 10000; i++) {
        CALayer *l = [[CALayer alloc] init];
        l.frame = CGRectMake([self random:390],[self random:390],10,10);
        l.backgroundColor = [UIColor blueColor].CGColor;
        l.borderColor = [UIColor orangeColor].CGColor;
        l.borderWidth = 2.0f;
        [v.layer addSublayer:l];
    }
    //add the view to the application's main view
    [self.view addSubview:v];
}

-(NSInteger)random:(NSInteger)value {
    srandomdev();
    return ((NSInteger)random())%value;
}

其次,创建一个方法来启动计时器并触发渲染...
-(void)touchesBegan {
    timer = [NSTimer scheduledTimerWithTimeInterval:0.03f target:self selector:@selector(printTime) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [self render];
}

-(void)printTime {
    NSLog(@"%d (main thread running)",++timerCallCount);
}

第三步,创建一个渲染循环并使用回调函数,在渲染完成后将图像放在屏幕上。
-(void)render {
    NSLog(@"render was called");
    //create the queue
    backgroundRenderQueue = dispatch_queue_create("backgroundRenderQueue",DISPATCH_QUEUE_CONCURRENT);    
    //create a async call on the background queue
    dispatch_async(backgroundRenderQueue, ^{
        //create a cgcontext
        NSUInteger width = (NSUInteger)v.frame.size.width;
        NSUInteger height = (NSUInteger)v.frame.size.height;
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        NSUInteger bytesPerPixel = 4;
        NSUInteger bytesPerRow = bytesPerPixel * width;
        unsigned char *rawData = malloc(height * bytesPerRow);
        NSUInteger bitsPerComponent = 8;
        CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

        //render the layer and its subviews
        [v.layer renderInContext:context];

        //create a callback async on the main queue when rendering is complete
        dispatch_async(dispatch_get_main_queue(), ^{
            //create an image from the context
            UIImage *m = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
            UIImageView *uiiv = [[UIImageView alloc] initWithImage:m];
            //add the image view to the main view
            [self.view addSubview:uiiv];
            CGColorSpaceRelease(colorSpace);
            CGContextRelease(context);
            NSLog(@"rendering complete");
            [timer invalidate];
        });
    });
}

注意:如果您的图层不在同一个子层中,您可以轻松地调用一个for循环,将上下文从每个CALayer的原点翻译到并从其原点绘制每个图层到上下文本身中。

当我运行这个程序时,我得到以下输出:

2013-03-18 07:14:28.617 C4iOS[21086:907] render was called
2013-03-18 07:14:28.648 C4iOS[21086:907] 1 (main thread running)
2013-03-18 07:14:28.680 C4iOS[21086:907] 2 (main thread running)
2013-03-18 07:14:28.709 C4iOS[21086:907] 3 (main thread running)
2013-03-18 07:14:28.737 C4iOS[21086:907] 4 (main thread running)
2013-03-18 07:14:28.767 C4iOS[21086:907] 5 (main thread running)
2013-03-18 07:14:28.798 C4iOS[21086:907] 6 (main thread running)
2013-03-18 07:14:28.828 C4iOS[21086:907] 7 (main thread running)
2013-03-18 07:14:28.859 C4iOS[21086:907] 8 (main thread running)
2013-03-18 07:14:28.887 C4iOS[21086:907] 9 (main thread running)
2013-03-18 07:14:28.917 C4iOS[21086:907] 10 (main thread running)
2013-03-18 07:14:28.948 C4iOS[21086:907] 11 (main thread running)
2013-03-18 07:14:28.978 C4iOS[21086:907] 12 (main thread running)
2013-03-18 07:14:29.010 C4iOS[21086:907] 13 (main thread running)
2013-03-18 07:14:29.037 C4iOS[21086:907] 14 (main thread running)
2013-03-18 07:14:29.069 C4iOS[21086:907] 15 (main thread running)
2013-03-18 07:14:29.097 C4iOS[21086:907] 16 (main thread running)
2013-03-18 07:14:29.130 C4iOS[21086:907] 17 (main thread running)
2013-03-18 07:14:29.159 C4iOS[21086:907] 18 (main thread running)
2013-03-18 07:14:29.189 C4iOS[21086:907] 19 (main thread running)
2013-03-18 07:14:29.217 C4iOS[21086:907] 20 (main thread running)
2013-03-18 07:14:29.248 C4iOS[21086:907] 21 (main thread running)
2013-03-18 07:14:29.280 C4iOS[21086:907] 22 (main thread running)
2013-03-18 07:14:29.309 C4iOS[21086:907] 23 (main thread running)
2013-03-18 07:14:29.337 C4iOS[21086:907] 24 (main thread running)
2013-03-18 07:14:29.369 C4iOS[21086:907] 25 (main thread running)
2013-03-18 07:14:29.397 C4iOS[21086:907] 26 (main thread running)
2013-03-18 07:14:29.405 C4iOS[21086:907] rendering complete

3

renderInContext: 是这里最好的工具,但你不需要在主线程上运行它。只需将其移至后台线程,就可以避免冻结应用程序。


如果您将renderInContext放在后台运行,可能会导致内存泄漏。即使应该自动释放一些quartz2d引用,但它们仍未被释放。Instruments不会显示泄漏的位置,但最终会收到内存警告,然后崩溃。 - mskw
renderInContext 也非常慢,我正在寻找一种替代方法,但目前还没有找到。 - mskw
绘图应该是线程安全的。您应该能够在后台线程中使用CGImageContext完成此操作。您需要释放任何创建的上下文,使用CGContextRelease方法。如果您认为它有问题,请向苹果提交一个radar报告:http://radar.apple.com。 - jackslash

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