【问题标题】:drawRect always wants to update the entire screen when view is shrunken当视图缩小时,drawRect总是想更新整个屏幕
【发布时间】:2012-06-22 16:58:00
【问题描述】:

我有一个 CGImage,它显示在由 UIScrollView 控制的 UIView 中。图像通常为 1680*1050,24 位颜色,无 Alpha 通道。

图像是这样创建的:

CGImageRef bitmapClass::CreateBitmap(int width, int height, int imageSize, int colorDepth, int bytesPerRow)
{
   unsigned char* m_pvBits = malloc(imageSize);

   // Initializt bitmap buffer to black (red, green, blue)
   memset(m_pvBits, 0, imageSize);

   m_DataProviderRef = 
      CGDataProviderCreateWithData(NULL, m_pvBits, imageSize, NULL); 

   m_ColorSpaceRef = 
      CGColorSpaceCreateDeviceRGB();   

   return
      CGImageCreate(width, height, 
                    8, //kBitsPerComponent 
                    colorDepth, 
                    bytesPerRow, 
                    m_ColorSpaceRef, 
                    kCGBitmapByteOrderDefault | kCGImageAlphaNone, 
                    m_DataProviderRef, 
                    NULL, false, kCGRenderingIntentDefault);
}

图片内容通过改变m_pvBits中的像素在后台定期更新,并在UIView中使用:

[myView setNeedsDisplayInRect:rect];

调用 drawRect 来显示图像,如下所示:

- (void)drawRect:(CGRect)rect
{
   CGRect imageRect;
   imageRect.origin = CGPointMake(0.0, 0.0);
   imageRect.size = CGSizeMake(self.imageWidth, self.imageHeight);

   CGContextRef context = UIGraphicsGetCurrentContext();

   CGContextSetInterpolationQuality(context, kCGInterpolationNone);

   // Drawing code
   CGContextDrawImage(context, imageRect, (CGImageRef)pWindow->GetImageRef());
}

只要视图没有缩小(缩小),这种方法就可以很好地工作。我知道'rect'实际上并没有直接在drawRect中使用,但'context'似乎知道CGContextDrawImage应该更新屏幕的哪个部分。

我的问题是,即使我只使用 setNeedsDisplayInRect 使屏幕的一小部分区域无效,但当视图缩小时,整个屏幕都会调用 drawRect。因此,如果我的图像是 1680*1050 并且我使一个小矩形 (x,y,w,h)=(512, 640, 32, 32) 无效,则使用 (x,y,w,h)=(0 , 0, 1680, 1050)。

为什么?

【问题讨论】:

    标签: ios uiscrollview drawrect cgcontextdrawimage


    【解决方案1】:

    Apple 提供了一个有趣的示例代码项目,可能会对您有所帮助。它被称为Large Image Downsizing。它做了很多事情,但真正有趣的部分是TiledImageView.m。其中,视图的图层类设置为CATiledLayer

    + (Cl ass)layerClass {
        return [CATiledLayer class];
    }
    

    tiledLayer 属性正在init 方法中设置:

    // Create a new TiledImageView with the desired frame and scale.
    -(id)initWithFrame:(CGRect)_frame image:(UIImage*)_image scale:(CGFloat)_scale {
        if ((self = [super initWithFrame:_frame])) {
            self.image = _image;
            imageRect = CGRectMake(0.0f, 0.0f, CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
            imageScale = _scale;
            CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
            // levelsOfDetail and levelsOfDetailBias determine how
            // the layer is rendered at different zoom levels.  This
            // only matters while the view is zooming, since once the 
            // the view is done zooming a new TiledImageView is created
            // at the correct size and scale.
            tiledLayer.levelsOfDetail = 4;
            tiledLayer.levelsOfDetailBias = 4;
            tiledLayer.tileSize = CGSizeMake(512.0, 512.0); 
        }
        return self;
    }
    

    最后,这是真正有趣的部分,这是 drawRect: 实现(NSLog 来自我):

    -(void)drawRect:(CGRect)_rect {
        CGContextRef context = UIGraphicsGetCurrentContext();    
        CGContextSaveGState(context);
        // Scale the context so that the image is rendered 
        // at the correct size for the zoom level.
        CGContextScaleCTM(context, imageScale,imageScale);  
        NSLog(@"rect: %@, imageScale: %.4f, clip: %@", NSStringFromCGRect(_rect), imageScale, NSStringFromCGRect(CGContextGetClipBoundingBox(context)));
        CGContextDrawImage(context, imageRect, image.CGImage);
        CGContextRestoreGState(context);    
    }
    

    drawRect: 为每个 512x512 瓦片调用。不过,传入的_rect 根本没有在方法内部使用。不必这样做,因为当前上下文的剪切路径设置为正确的区域(正如您将在 CGContextGetClipBoundingBox 的输出中看到的那样)。所以就像 Rob 说的,CGContextDrawImage 可以使用,并且只会绘制大图像的请求“平铺”,尽管看起来整个图像一次又一次地绘制。如果您不使用CATiledLayer,则确实会一次又一次地绘制整个图像,这会减慢一切。但是当您使用CATiledLayer 时,只会绘制当前需要的“图块”,这会加快速度。

    我希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      谢谢你的回答。

      据我观察,传递给 drawRect 的 rect 参数的内容与我查询 CGContextGetClipBoundingBox() 时得到的内容相同。

      您建议我应该将我的绘图优化到剪裁范围 - 但我没有在这里进行任何计算。我只是在调用 CGContextDrawImage()。我可以看到其他程序员抱怨 CGContextDrawImage 的性能不佳,而且它显然从 iPhone 3 到 iPhone 4 变慢了。我当然希望性能更好,但现在最大的问题是 iOS 更新整个图像,即使只有一小部分无效(使用 setNeedsDisplay)。

      缩小是指我可以看到更多图像,但图像缩小了。我不会经常放大和缩小,但图像的一小部分会定期更新并调用 setNeedsDisplay。当我缩放或平移时,它非常缓慢且突然,因为整个屏幕都是由 iOS 设置为更新的。

      你说得对,CGContextDrawImage 优化了实际绘制的内容——我可以通过测量操作所花费的时间来看到这一点。

      如您所见,我使用 malloc 分配图像位。还有其他方法可以分配更适合图形处理器的图像位吗?

      CGImageCreate 是否有更优化的 ColorRef 模式或其他标志?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-14
        相关资源
        最近更新 更多