简体   繁体   English

drawRect总是希望在缩小视图时更新整个屏幕

[英]drawRect always wants to update the entire screen when view is shrunken

I have a CGImage that is displayed in an UIView controlled by a UIScrollView. 我有一个CGImage,显示在由UIScrollView控制的UIView中。 The image is typically 1680*1050 in 24 bit colors without alpha channel. 图像通常是1680 * 1050,24位颜色,没有alpha通道。

The image is created like this: 图像创建如下:

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);
}

The image content is updated regularly in the background by changing the pixels in m_pvBits and updated in the UIView using: 通过更改m_pvBits中的像素并使用以下内容在UIView中更新,在后台定期更新图像内容:

[myView setNeedsDisplayInRect:rect];

That invokes drawRect that displays the image like this: 这会调用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());
}

This works well as long as the view is not shrunk (zoomed out). 只要视图没有缩小(缩小),这就可以正常工作。 I know that 'rect' is actually not used directly in drawRect but the 'context' seems to know what part of the screen CGContextDrawImage should update. 我知道'rect'实际上并没有直接在drawRect中使用,但'context'似乎知道CGContextDrawImage应该更新的屏幕部分。

My problem is that even though I only invalidate a small area of the screen with setNeedsDisplayInRect, drawRect is called with the entire screen when the view is shrunk. 我的问题是,即使我只使用setNeedsDisplayInRect使屏幕的一小部分区域无效,当视图缩小时,整个屏幕也会调用drawRect。 So if my image is 1680*1050 and I invalidates a small rectangle (x,y,w,h)=(512, 640, 32, 32), drawRect is called with (x,y,w,h)=(0, 0, 1680, 1050). 因此,如果我的图像是1680 * 1050并且我使一个小矩形(x,y,w,h)=(512,640,32,32)无效,则使用(x,y,w,h)=(0)调用drawRect ,0,1680,1050)。

Why? 为什么?

There is an interesting sample code project from Apple that might help you. Apple提供了一个有趣的示例代码项目,可能对您有所帮助。 It's called Large Image Downsizing . 它被称为大图缩小 It does a bunch of things, but the really interesting part is TiledImageView.m . 它做了很多事情,但真正有趣的部分是TiledImageView.m In it, the view's layer class is set to CATiledLayer : 在其中,视图的图层类设置为CATiledLayer

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

The tiledLayer properties are being set up in the init method: 正在init方法中设置tilesLayer属性:

// 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;
}

and finally, and this is the really interesting part, this is the drawRect: implementation (NSLog is from me): 最后,这是真正有趣的部分,这是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: is called for every 512x512 tile. drawRect:为每个512x512磁贴调用。 The passed in _rect is not used at all inside the method, though. 但是,传入的_rect在方法中_rect没有使用。 It doesn't have to, because the clipping path of the current context is set to the correct region (as you'll see by the output of CGContextGetClipBoundingBox ). 它不必,因为当前上下文的剪切路径设置为正确的区域(您将通过CGContextGetClipBoundingBox的输出看到)。 So like Rob said, CGContextDrawImage can be used and only the requested "tile" of the large image will be drawn, although it looks as if the whole image is drawn again and again. 就像Rob说的那样,可以使用CGContextDrawImage ,只会绘制大图像的请求“图块”,尽管看起来好像整个图像一次又一次地被绘制。 If you don't use CATiledLayer , the whole image indeed is drawn again and again, which slows everything down. 如果您不使用CATiledLayer ,则会一次又一次地绘制整个图像,从而减慢所有内容的速度。 But when you use CATiledLayer , only the currently needed "tiles" are drawn which makes it a lot faster. 但是当你使用CATiledLayer ,只会绘制当前需要的“图块”,这会使它更快。

I hope this helps. 我希望这有帮助。

Thank you for the answer. 谢谢你的回答。

It is my observation that the content of the rect parameter that is passed to drawRect is the same as I get when I query CGContextGetClipBoundingBox(). 我的观察结果是传递给drawRect的rect参数的内容与我查询CGContextGetClipBoundingBox()时得到的内容相同。

You suggest that I should optimize my drawing to the clipping bounds - but I'm not doing any calculations here. 你建议我应该将我的绘图优化到剪切边界 - 但我在这里没有做任何计算。 I'm just calling CGContextDrawImage(). 我只是在调用CGContextDrawImage()。 I can see other programmers complaining about poor performance of CGContextDrawImage and that it apparently got much slower from iPhone 3 to 4. I would of course like the performance to be better but right now the biggest problem is that iOS updating the entire image even though only a small part is invalidated (with setNeedsDisplay). 我可以看到其他程序员抱怨CGContextDrawImage性能不佳,而且从iPhone 3到4显然慢得多。我当然希望性能更好但是现在最大的问题是iOS更新整个图像即使只是一小部分无效(使用setNeedsDisplay)。

By zoomed out I mean that I'm able to see more of the image but the image is shrunk. 通过缩小我的意思是我能够看到更多的图像,但图像缩小了。 I'm not constantly zooming in and out but a small part of the image is regularly updated and invoking setNeedsDisplay. 我不是一直在放大和缩小,但是图像的一小部分会定期更新并调用setNeedsDisplay。 When I zoom or when I pan it is very slow and abrupt because the entire screen is set by iOS to be updated. 当我缩放或平移时它非常缓慢且突然,因为整个屏幕由iOS设置以进行更新。

You are right that CGContextDrawImage optimizes what is actually drawn - I can see that by measuring the time the operation takes. 你是对的,CGContextDrawImage优化了实际绘制的内容 - 我可以通过测量操作所需的时间来看到它。

As you can see I allocate my image bits with malloc. 如您所见,我使用malloc分配我的图像位。 Is there another way that I can allocate the image bits that is better optimized for the graphics processor? 还有另一种方法可以分配更好地优化图形处理器的图像位吗?

Is there a more optimal ColorRef mode or other flags for CGImageCreate? 是否有更优化的ColorRef模式或CGImageCreate的其他标志?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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