33

CGContextDrawImage(CGContextRef、CGRect、CGImageRef)は、UIImageをサポートするCGImageを描画する場合よりも、CoreGraphicsによって(つまり、CGBitmapContextCreateImageを使用して)作成されたCGImageを描画する場合に非常に悪いパフォーマンスを示すようです。このテスト方法を参照してください。

-(void)showStrangePerformanceOfCGContextDrawImage
{
    ///Setup : Load an image and start a context:
    UIImage *theImage = [UIImage imageNamed:@"reallyBigImage.png"];
    UIGraphicsBeginImageContext(theImage.size);
    CGContextRef ctxt = UIGraphicsGetCurrentContext();    
    CGRect imgRec = CGRectMake(0, 0, theImage.size.width, theImage.size.height);


    ///Why is this SO MUCH faster...
    NSDate * startingTimeForUIImageDrawing = [NSDate date];
    CGContextDrawImage(ctxt, imgRec, theImage.CGImage);  //Draw existing image into context Using the UIImage backing    
    NSLog(@"Time was %f", [[NSDate date] timeIntervalSinceDate:startingTimeForUIImageDrawing]);

    /// Create a new image from the context to use this time in CGContextDrawImage:
    CGImageRef theImageConverted = CGBitmapContextCreateImage(ctxt);

    ///This is WAY slower but why??  Using a pure CGImageRef (ass opposed to one behind a UIImage) seems like it should be faster but AT LEAST it should be the same speed!?
    NSDate * startingTimeForNakedGImageDrawing = [NSDate date];
    CGContextDrawImage(ctxt, imgRec, theImageConverted);
    NSLog(@"Time was %f", [[NSDate date] timeIntervalSinceDate:startingTimeForNakedGImageDrawing]);


}

ですから、問題は、#1何がこれを引き起こしているのか、#2はそれを回避する方法、つまり、より高速なCGImageRefを作成する他の方法があるのか​​ということだと思います。最初にすべてをUIImagesに変換できることに気付きましたが、それは非常に醜い解決策です。私はすでにCGContextRefをそこに置いています。

更新:これは小さな画像を描くときに必ずしも当てはまらないようですか?これは、大きな画像(つまりフルサイズのカメラ写真)を使用すると、この問題が増幅されるという手がかりになる可能性があります。640x480は、どちらの方法でも実行時間の点でかなり似ているようです

更新2:わかりました、それで私は何か新しいものを発見しました..それは実際にはパフォーマンスを変えているCGImageの裏付けではありません。2つのステップの順序をフリップフロップして、UIImageメソッドの動作を遅くすることができますが、「裸の」CGImageは超高速になります。あなたが2番目に実行するものはどれでもひどいパフォーマンスに苦しむようです。これは、CGBitmapContextCreateImageで作成した画像に対してCGImageReleaseを呼び出してメモリを解放しない限り、当てはまるようです。その後、UIImageでバックアップされたメソッドは高速になります。逆にそれは真実ではありません。何が得られますか?「混雑した」メモリは、このようなパフォーマンスに影響を与えるべきではありませんか?

更新3:話が早すぎた。以前の更新は、サイズ2048x2048の画像にも当てはまりますが、1936x2592(カメラサイズ)にステップアップすると、操作の順序やメモリの状況に関係なく、ネイキッドCGImageメソッドの速度が大幅に低下します。21MBの画像を効率的に処理できないのに対し、16MBの画像を効率的にするCGの内部制限があるかもしれません。2048x2048よりもカメラサイズの描画が文字通り20倍遅くなります。どういうわけか、UIImageは純粋なCGImageオブジェクトよりもはるかに高速にCGImageデータを提供します。oO

UPDATE 4:これはメモリキャッシュの問題に関係しているのではないかと思いましたが、UIImageが非キャッシュの[UIImage imageWithContentsOfFile]でロードされても、[UIImageimageNamed]が使用されている場合と同じ結果になります。

更新5(2日目):昨日回答されたよりもmroeの質問を作成した後、今日は何かしっかりしたものがあります。私が確かに言えることは次のとおりです。

  • UIImageの背後にあるCGImageはアルファを使用しません。(kCGImageAlphaNoneSkipLast)。私のコンテキストはアルファを使用していたので、おそらくそれらはより速く描画されると思いました。そこで、kCGImageAlphaNoneSkipLastを使用するようにコンテキストを変更しました。これにより、次の場合を除いて、描画がはるかに高速になります。
  • 最初にUIImageを使用してCGContextRefに描画すると、後続のすべての画像の描画が遅くなります

私はこれを1)最初に非アルファコンテキスト(1936x2592)を作成することによって証明しました。2)ランダムに色付けされた2x2の正方形で塗りつぶしました。3)そのコンテキストにCGImageを描画するフルフレームは高速(.17秒)でした。4)実験を繰り返しましたが、UIImageを裏付ける描画されたCGImageでコンテキストを埋めました。その後のフルフレーム画像の描画は6秒以上でした。SLOWWWWW。

どういうわけか、(大きい)UIImageを使用してコンテキストに描画すると、そのコンテキストへの後続のすべての描画が大幅に遅くなります。

4

2 に答える 2

46

たくさんの実験を経て、私はこのような状況を処理するための最速の方法を見つけたと思います。上記の描画操作は6秒以上かかっていましたが、現在は.1秒です。はい。これが私が発見したものです:

  • ピクセル形式でコンテキストと画像を均質化します!私が尋ねた質問の根本は、UIImage内のCGImageが同じピクセル形式をコンテキストとして使用しているという事実に要約されます。したがって、高速です。CGImagesは別の形式であったため、低速でした。CGImageGetAlphaInfoを使用して画像を検査し、使用しているピクセル形式を確認します。アルファを使用する必要がないため、現在はどこでもkCGImageAlphaNoneSkipLastを使用しています。どこでも同じピクセル形式を使用しない場合、コンテキストに画像を描画するときに、Quartzは各ピクセルに対して高価なピクセル変換を実行することを余儀なくされます。=遅い

  • CGLayersを使用してください!これらにより、オフスクリーン描画のパフォーマンスが大幅に向上します。これがどのように機能するかは基本的に次のとおりです。1)CGLayerCreateWithContextを使用して、コンテキストからCGLayerを作成します。2)CGLayerGetContextで取得したこのレイヤーのコンテキストで描画/描画プロパティの設定を行います。元のコンテキストからピクセルまたは情報を読み取ります。3)完了したら、CGContextDrawLayerAtPointを使用して、このCGLayerを元のコンテキストに「スタンプ」します。これは、次の点に注意する限り高速です。

  • 1)CGContextDrawLayerAtPointを使用してレイヤーをCGContextRefに「スタンプ」する前に、コンテキストから作成されたCGImage(つまり、CGBitmapContextCreateImageで作成されたCGImage)を解放します。これにより、そのレイヤーを描画するときに3〜4倍の速度が向上します。2)どこでも同じピクセルフォーマットを維持してください!! 3)できるだけ早くCGオブジェクトをクリーンアップします。おそらくこれらの強力な参照に関連付けられたコールバックまたはチェックがあるため、メモリ内でぶらぶらしているものは、速度低下の奇妙な状況を作成しているようです。推測ですが、メモリをできるだけ早くクリーンアップすると、パフォーマンスが大幅に向上すると言えます。

于 2011-09-29T15:36:05.943 に答える
15

私も同様の問題を抱えていました。私のアプリケーションは、画面サイズとほぼ同じ大きさの画像を再描画する必要があります。問題は、同じ解像度の2つの画像をできるだけ速く描画することでした。回転も反転もせず、毎回画面の異なる場所に拡大縮小して配置しました。結局のところ、私はiPad 1で最大15〜20 FPS、iPad4で最大20〜25FPSを取得することができました。だから...これが誰かを助けることを願っています:

  1. タイプライターが言ったように、同じピクセルフォーマットを使用する必要があります。AlphaNoneで使用すると、速度が向上します。しかし、さらに重要なのは、私の場合のargb32_image呼び出しは、ピクセルをARGBからBGRAに変換する多数の呼び出しを行ったことです。したがって、私にとって最高のbitmapInfo値は(当時、Appleが将来ここで何かを変更できる可能性があります)でした: const CGBitmabInfo g_bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast;
  2. 長方形の引数が(CGRectIntegralによって)整数にされた場合、CGContextDrawImageはより速く動作する可能性があります。画像を1に近い係数で拡大縮小すると、より効果があるようです。
  3. レイヤーを使用すると、実際に速度が低下しました。おそらく、2011年以降、一部の社内通話で何かが変更された可能性があります。
  4. (CGContextSetInterpolationQualityによって)デフォルトよりも低いコンテキストの補間品質を設定することが重要です。を使用することをお勧めし(IS_RETINA_DISPLAY ? kCGInterpolationNone : kCGInterpolationLow)ます。マクロIS_RETINA_DISPLAYはここから取得されます。
  5. コンテキストを作成するときは、必ずCGColorSpaceCreateDeviceRGB()などからCGColorSpaceRefを取得してください。デバイスの色空間を要求する代わりに固定色空間を取得することについて、いくつかのパフォーマンスの問題が報告されました。
  6. UIImageViewからビュークラスを継承し、コンテキストから作成された画像にself.imageを設定するだけで便利であることがわかりました。ただし、これを実行する場合は、最初にUIImageViewの使用についてお読みください。これは、コードロジックを変更する必要があるためです(drawRect:はもう呼び出されないため)。
  7. また、実際の描画時に画像の拡大縮小を回避できる場合は、拡大縮小を試みてください。拡大縮小されていない画像を描画する方がはるかに高速です。残念ながら、それは選択肢ではありませんでした。
于 2013-04-22T09:04:50.317 に答える