CGContextDrawImage est EXTRÊMEMENT lente après la grande UIImage entraînée
Il semble que CGContextDrawImage(CGContextRef, CGRect, CGImageRef) effectue BIEN PIRE lors de l'élaboration d'un CGImage qui a été créé par CoreGraphics (c'est à dire avec CGBitmapContextCreateImage) que lors de l'élaboration de la CGImage qui soutient une UIImage. Voir cette méthode d'analyse:
-(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]);
}
Donc je suppose que la question est, #1 ce qui peut être à l'origine de ce et n ° 2 est-il un moyen de contourner cela, c'est à dire d'autres façons de créer un CGImageRef qui peut être plus rapide? Je me rends compte que je pouvais tout convertir UIImages premier mais c'est un vilain solution. J'ai déjà le CGContextRef assis là.
Mise à JOUR : Ce qui semble ne pas nécessairement être vrai lors de l'élaboration de petites images? Qui peut être un indice - que ce problème est amplifié lorsque des images de grande taille (c'est à dire pleine taille de la caméra photos) sont utilisés. 640 x 480 semble être assez similaire en termes de temps d'exécution avec la méthode
Mise à JOUR 2 : Ok, j'ai découvert quelque chose de nouveau.. Sa fait PAS le soutien de la CGImage qui est de modifier la performance. Je peux flip-flop de l'ordre de 2 étapes et de faire de la UIImage méthode se comporter lentement, tandis que le "nu" CGImage sera super rapide. Il semble selon ce que vous effectuez deuxième à souffrir de terribles performance. Cela semble être le cas, à MOINS de me libérer de la mémoire en appelant CGImageRelease sur l'image que j'ai créé avec CGBitmapContextCreateImage. Puis le UIImage soutenu méthode sera rapide par la suite. À l'inverse, il n'est pas vrai. Ce qui donne? "Bondé" la mémoire ne devrait pas affecter les performances de ce genre, devrait-il?
Mise à JOUR 3 : parle trop vite. La mise à jour précédente est vraie pour les images à la taille de 2048x2048 pixels, mais le renforcement de la 1936x2592 (pour l'appareil), le nu CGImage méthode est plus lente, quel que soit l'ordre d'exploitation ou la situation de mémoire. Peut-être il y a quelques CG limites internes qui font un 16MB image efficace alors que la 21MB image ne peut pas être gérées efficacement. Ses littéralement 20 fois plus lent à tirer la caméra que 2048x2048. En quelque sorte UIImage offre à ses CGImage de données beaucoup plus rapide qu'un pur CGImage objet n'. o.O
Mise à JOUR 4 : j'ai pensé que cela pourrait avoir à faire avec un peu de mise en mémoire cache chose, mais le résultat est le même, que la UIImage est chargé avec la non-mise en cache [UIImage imageWithContentsOfFile] comme si [UIImage imageNamed] est utilisé.
Mise à JOUR 5 (Jour 2) : Après la création de manière plus de questions que de a répondu, hier, je l'ai quelque chose solide aujourd'hui. Ce que je peux dire pour sûr, c'est la suivante:
- La CGImages derrière une UIImage ne pas utiliser de l'alpha. (kCGImageAlphaNoneSkipLast). J'ai pensé que peut-être qu'ils ont été les plus rapides à être établi en raison de mon contexte à l'aide de l'alpha. J'ai donc modifié le contexte de l'utilisation kCGImageAlphaNoneSkipLast. Cela rend le dessin BEAUCOUP plus rapide, à MOINS que:
- Dessin dans un CGContextRef avec une UIImage PREMIER, fait TOUS les de dessin d'image lente
J'ai prouvé par 1)d'abord, la création d'un non-alpha contexte (1936x2592). 2) il a Rempli de façon aléatoire de couleur 2x2 places. 3) Full frame dessin un CGImage dans ce contexte a été RAPIDE (.17 secondes) 4) a Répété l'expérience, mais rempli de contexte avec une CGImage à l'appui d'une UIImage. Ultérieure image complète de dessin a+ de 6 secondes. SLOWWWWW.
En quelque sorte de dessin dans un contexte avec un (Grand) UIImage ralentit considérablement tous les dessin dans ce contexte.
- Sur quel appareil avez-vous tester? 2k x 2k pixels des images des besoins ~16 mo de mémoire, alors que sur l'iPhone 3G il y a seulement 30 mo de mémoire libre pour l'application.
- Tout cela est fait sur un iPhone 4 dans un bac à sable application de test... je suis certain de ne pas obtenir des avertissements de mémoire.
Vous devez vous connecter pour publier un commentaire.
Bien après une TONNE d'expérimentation, je pense que j'ai trouvé le moyen le plus rapide pour gérer ce genre de situation. Le dessin ci-dessus qui a été prise+ de 6 secondes maintenant .1 secondes. OUI. Voici ce que j'ai découvert:
Homogénéiser votre contextes & images avec un format de pixel! La racine de la question que j'avais posée résumait au fait que la CGImages à l'intérieur d'une UIImage ont été en utilisant LE MÊME FORMAT de PIXEL que mon contexte. Donc rapide. Le CGImages ont un format différent et donc lent. Inspecter vos images avec CGImageGetAlphaInfo pour voir quel format de pixel qu'ils utilisent. Je suis en utilisant kCGImageAlphaNoneSkipLast PARTOUT maintenant que je n'ai pas besoin de travailler avec alpha. Si vous n'utilisez pas le même format de pixel partout, lors de l'élaboration d'une image dans un contexte de Quartz sera forcé de faire cher du pixel nombre de conversions pour CHAQUE pixel. = LENT
UTILISATION CGLayers! Ces rendre à l'écran de dessin de performance bien meilleure. Comment cela fonctionne est essentiellement comme suit. 1) créer un CGLayer à partir du contexte à l'aide de CGLayerCreateWithContext. 2) faire un dessin/définition des propriétés de dessin sur CETTE COUCHE de CONTEXTE qui est obtenu avec CGLayerGetContext. LIRE tous les pixels ou des informations de leur contexte ORIGINAL. 3) Lorsque vous avez terminé, "timbre" ce CGLayer de retour sur le contexte d'origine à l'aide de CGContextDrawLayerAtPoint.C'est RAPIDE aussi longtemps que vous gardez à l'esprit:
1) la Mainlevée de toute CGImages créé à partir d'un contexte (c'est à dire ceux créés avec CGBitmapContextCreateImage) AVANT d'estampage de votre couche de retour dans le CGContextRef à l'aide de CGContextDrawLayerAtPoint. Cela crée un 3-4x augmentation de la vitesse lors de l'élaboration de cette couche. 2) Gardez votre format de pixel la même partout!! 3) Nettoyer CG objets DÈS QUE vous le pouvez. Les choses traîner dans la mémoire semblent créer d'étranges situations de ralentissement, probablement parce qu'il y a des rappels ou des contrôles associés à ces références fortes. Juste une supposition, mais je peux dire que le NETTOYAGE de la MÉMOIRE ASAP aide considérablement les performances.
J'ai eu un problème similaire. Ma demande a pour redessiner une image presque aussi grand que la taille de l'écran. Le problème se résumait à dessin aussi vite que possible, deux images de la même résolution, ni tourné ni retourné, mais à une échelle et placés dans différents endroits de l'écran à chaque fois. Après tout, j'ai été en mesure d'obtenir ~15-20 FPS sur iPad 1 et ~20-25 FPS sur iPad4. Alors... espérons que cela aide quelqu'un:
const CGBitmabInfo g_bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast;
(IS_RETINA_DISPLAY ? kCGInterpolationNone : kCGInterpolationLow)
. Les Macros IS_RETINA_DISPLAY est pris de ici.