drawRect siempre quiere actualizar toda la pantalla cuando la vista se networkinguce

Tengo una CGImage que se muestra en una UIView controlada por un UIScrollView. La image suele ser 1680 * 1050 en colors de 24 bits sin canal alfa.

La image se crea así:

CGImageRef bitmapClass::CreateBitmap(int width, int height, int imageSize, int colorDepth, int bytesPerRow) { unsigned char* m_pvBits = malloc(imageSize); // Initializt bitmap buffer to black (networking, 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); } 

El contenido de la image se actualiza periódicamente en segundo plano al cambiar los píxeles en m_pvBits y se actualiza en UIView utilizando:

 [myView setNeedsDisplayInRect:rect]; 

Eso invoca drawRect que muestra la image así:

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

Esto funciona bien siempre y cuando la vista no se contraiga (se aleje). Sé que 'rect' en realidad no se utiliza directamente en drawRect, pero el 'context' parece saber qué parte de la pantalla CGContextDrawImage debería actualizar.

Mi problema es que, aunque solo invalido un área pequeña de la pantalla con setNeedsDisplayInRect, se llama a drawRect con la pantalla completa cuando la vista se contrae. Entonces si mi image es 1680 * 1050 e invalido un pequeño rectángulo (x, y, w, h) = (512, 640, 32, 32), se llama a drawRect con (x, y, w, h) = (0 , 0, 1680, 1050).

¿Por qué?

Hay un interesante proyecto de código de ejemplo de Apple que podría ayudarte. Se llama Reducción de image de gran tamaño . Hace un montón de cosas, pero la parte realmente interesante es TiledImageView.m . En él, la class de capa de la vista se establece en CATiledLayer :

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

Las properties tiledLayer se configuran en el método init :

 // Create a new TiledImageView with the desinetworking 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 rendenetworking 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; } 

y finalmente, y esta es la parte realmente interesante, este es el drawRect: implementación (NSLog es de mí):

 -(void)drawRect:(CGRect)_rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); // Scale the context so that the image is rendenetworking // 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: se llama para cada baldosa 512×512. Sin _rect pasada en _rect no se usa en absoluto dentro del método. No es necesario, ya que la ruta de recorte del context actual se establece en la región correcta (como verá en la salida de CGContextGetClipBoundingBox ). Así como dijo Rob, se puede usar CGContextDrawImage y solo se CGContextDrawImage el "mosaico" solicitado de la image grande, aunque parece que toda la image se dibuja una y otra vez. Si no utiliza CATiledLayer , la image completa se dibuja una y otra vez, lo que ralentiza todo. Pero cuando usa CATiledLayer , solo se dibujan las "fichas" actualmente necesarias, lo que hace que sea mucho más rápido.

Espero que esto ayude.

Gracias por la respuesta.

Es mi observación que el contenido del parámetro rect que se pasa a drawRect es el mismo que obtengo cuando busco CGContextGetClipBoundingBox ().

Sugieres que debería optimizar mi dibujo a los límites de recorte, pero no estoy haciendo ningún cálculo aquí. Solo estoy llamando a CGContextDrawImage (). Puedo ver a otros progtwigdores quejándose del pobre performance de CGContextDrawImage y que aparentemente se hizo mucho más lento desde el iPhone 3 a 4. Por supuesto, me gustaría que el performance fuera mejor, pero ahora el mayor problema es que iOS actualiza toda la image, aunque solo una parte pequeña se invalida (con setNeedsDisplay).

Al hacer un zoom, quiero decir que puedo ver más de la image, pero la image se networkinguce. No estoy constantemente acercando y alejando, pero una pequeña parte de la image se actualiza regularmente e invoca setNeedsDisplay. Cuando hago zoom o cuando hago un paneo, es muy lento y abrupto porque la pantalla completa está configurada por iOS para actualizarse.

Tiene razón en que CGContextDrawImage optimiza lo que realmente se dibuja, puedo verlo midiendo el time que toma la operación.

Como puedes ver, asigno mis bits de image con malloc. ¿Hay otra forma de asignar los bits de image mejor optimizados para el procesador de charts?

¿Hay un modo ColorRef más óptimo u otros indicadores para CGImageCreate?