3

UIScrollView によって制御される UIView に表示される CGImage があります。画像は通常、アルファ チャネルなしの 24 ビット カラーで 1680*1050 です。

イメージは次のように作成されます。

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で直接使用されていないことは知っていますが、「コンテキスト」は画面のどの部分をCGContextDrawImageで更新する必要があるかを知っているようです。

私の問題は、setNeedsDisplayInRect で画面の小さな領域のみを無効にしていても、ビューが縮小されると画面全体で drawRect が呼び出されることです。したがって、画像が 1680*1050 で、小さな四角形 (x,y,w,h)=(512, 640, 32, 32) を無効にする場合、drawRect は (x,y,w,h)=(0 で呼び出されます。 、0、1680、1050)。

なんで?

4

2 に答える 2

3

Apple からの興味深いサンプル コード プロジェクトが参考になるかもしれません。これはラージ イメージ ダウンサイジングと呼ばれます。それはたくさんのことをしますが、本当に興味深い部分はTiledImageView.m. その中で、ビューのレイヤ クラスは次のように設定されていCATiledLayerます。

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

inittiledLayer プロパティはメソッドで設定されています。

// 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。ロブが言ったように、CGContextDrawImage使用でき、大きな画像の要求された「タイル」のみが描画されますが、画像全体が何度も描画されているように見えます。を使用しないCATiledLayerと、画像全体が実際に何度も描画されるため、すべてが遅くなります。しかし、 を使用するCATiledLayerと、現在必要な「タイル」のみが描画されるため、はるかに高速になります。

これが役立つことを願っています。

于 2012-06-26T07:33:14.673 に答える
1

答えてくれてありがとう。

drawRectに渡されるrectパラメーターの内容は、CGContextGetClipBoundingBox()を照会したときに取得するものと同じであることが私の観察です。

描画をクリッピング境界に最適化する必要があると提案されましたが、ここでは計算を行っていません。CGContextDrawImage()を呼び出しているだけです。他のプログラマーがCGContextDrawImageのパフォーマンスの低下について不満を言っているのを見ることができ、iPhone 3から4にかけてパフォーマンスが大幅に低下したようです。もちろんパフォーマンスを向上させたいのですが、現時点で最大の問題は、iOSが画像全体を更新しているだけなのに小さな部分が無効になります(setNeedsDisplayを使用)。

ズームアウトすると、より多くの画像を見ることができますが、画像は縮小されます。常にズームインおよびズームアウトしているわけではありませんが、画像のごく一部が定期的に更新され、setNeedsDisplayが呼び出されます。ズームまたはパンすると、画面全体がiOSによって更新されるように設定されているため、画面全体が非常に遅く突然表示されます。

CGContextDrawImageが実際に描画されるものを最適化するのは正しいことです。操作にかかる時間を測定することで、それを確認できます。

ご覧のとおり、画像ビットをmallocで割り当てています。グラフィックプロセッサ用に最適化された画像ビットを割り当てることができる別の方法はありますか?

CGImageCreateに最適なColorRefモードやその他のフラグはありますか?

于 2012-06-24T21:52:14.593 に答える