从CALayers获取位图,以及renderInContext的任何替代方法

时间:2011-08-27 18:40:24

标签: iphone objective-c ios core-animation quartz-core

我有很多CALayer在我的应用程序运行时动态创建,我需要能够生成这些的一个位图,稍后将被屏蔽。

当我需要创建蒙版时,CALayers已经被绘制到背景中(也使用了shouldRasterize = YES),并且使用renderInContext我能够获得位图。但是,随着CAlayers数量的增加,renderInContext引起的暂停变得越来越长。有没有我可以使用renderInContext的替代方法,或者我可以使用它来阻止我的应用暂时冻结?

理想情况是直接从内存/缓冲区/缓存中访问已经绘制的像素数据而不使用OpenGL,但我不确定CoreAnimation是否可以实现。

谢谢,任何其他信息都非常有用!

2 个答案:

答案 0 :(得分:8)

Rob认为renderInContext:是在这里使用的正确方法是正确的。渲染上下文实际上将图层的像素数据渲染到上下文中。这是一个示例应用程序,它将在后台线程上绘制10,000个图层......

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

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

    首先,创建一个包含大量图层的子视图:

    @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的原点相互转换,并将每个图层单独绘制到上下文中< / p>

    当我运行时,我得到以下输出:

    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
    

答案 1 :(得分:3)

renderInContext:是这里最好的工具,但您不需要在主线程上运行它。只需将其移至后台线程,它就会停止冻结您的应用。