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データを提供します。 o.O
更新4:これは何らかのメモリキャッシュの問題に関係しているのではないかと思いましたが、UIImageが非キャッシュの[UIImage imageWithContentsOfFile]でロードされても、[UIImageimageNamed]が使用されている場合と同じ結果になります。
更新5(2日目):昨日答えられたよりもmroeの質問を作成した後、私は何か今日はしっかりしています。私が確かに言えることは次のとおりです。
私はこれを1)最初に非アルファコンテキスト(1936x2592)を作成することによって証明しました。 2)ランダムに色付けされた2x2の正方形で塗りつぶしました。 3)そのコンテキストにCGImageを描画するフルフレームは高速(.17秒)でした。4)実験を繰り返しましたが、UIImageを裏付ける描画されたCGImageでコンテキストを埋めました。その後のフルフレーム画像の描画は6秒以上でした。 SLOWWWWW。
どういうわけか、(大)UIImageを使用してコンテキストに描画すると、そのコンテキストへの後続のすべての描画が大幅に遅くなります。
たくさんの実験の後、私はこのような状況を処理するための最速の方法を見つけたと思います。 6秒以上かかっていた描画操作が0.1秒になりました。はい。これが私が発見したものです:
ピクセル形式でコンテキストと画像を均質化します!私が尋ねた質問の根本は、UIImage内のCGImageが私のコンテキストとして同じピクセルフォーマットを使用していたという事実に要約されます。したがって、高速です。 CGImagesは異なる形式であったため、低速でした。 CGImageGetAlphaInfoを使用して画像を検査し、使用しているピクセル形式を確認します。アルファを使用する必要がないため、現在はどこでもkCGImageAlphaNoneSkipLastを使用しています。どこでも同じピクセル形式を使用しない場合、コンテキストに画像を描画すると、Quartzは各ピクセルに対して高価なピクセル変換を実行することを余儀なくされます。 =遅い
CGLayersを使用してください!これらにより、オフスクリーン描画のパフォーマンスが大幅に向上します。これがどのように機能するかは基本的に次のとおりです。 1)CGLayerCreateWithContextを使用して、コンテキストからCGLayerを作成します。 2)CGLayerGetContextで取得したこのレイヤーのコンテキストで描画/描画プロパティの設定を行います。元のコンテキストからピクセルまたは情報を読み取ります。 3)完了したら、CGContextDrawLayerAtPointを使用して、このCGLayerを元のコンテキストに「スタンプ」します。これは、次の点に注意する限り高速です。
1)CGContextDrawLayerAtPointを使用してレイヤーをCGContextRefに「スタンプ」する前に、コンテキストから作成されたCGImage(つまり、CGBitmapContextCreateImageで作成されたもの)を解放します。これにより、そのレイヤーを描画するときに3〜4倍の速度が向上します。 2)どこでも同じピクセルフォーマットを維持してください!! 3)CGオブジェクトをできるだけ早くクリーンアップします。おそらくこれらの強力な参照に関連付けられたコールバックまたはチェックがあるため、メモリ内でぶらぶらしているものは、速度低下の奇妙な状況を作成しているようです。推測ですが、メモリをできるだけ早くクリーンアップすると、パフォーマンスが大幅に向上すると言えます。
私も同様の問題を抱えていました。私のアプリケーションは、画面サイズとほぼ同じ大きさの画像を再描画する必要があります。問題は、同じ解像度の2つの画像をできるだけ速く描画することでした。回転も反転もせず、毎回画面の異なる場所に拡大縮小して配置しました。結局のところ、iPad 1では最大15〜20 FPS、iPad4では最大20〜25FPSを得ることができました。だから...これが誰かを助けることを願っています:
const CGBitmabInfo g_bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast;
(IS_RETINA_DISPLAY ? kCGInterpolationNone : kCGInterpolationLow)
の使用をお勧めします。マクロIS_RETINA_DISPLAYは ここ から取得されます。