Próbuję użyć CAShapeLayer
do maskowania CALayer
w moim iOS
aplikacji jak trwa ułamek CPU time
aby maskować obraz vs ręcznie maskowania jeden w bitmap context
;problemy z wydajnością przy używaniu wiele masek CALayer
Gdy mam kilkadziesiąt lub więcej zdjęć nałożonych na siebie nawzajem, CAShapeLayer
zamaskowany UIImageView
powoli porusza się w dotyku.
Oto przykładowy kod:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"SomeImage.jpg" ofType:nil]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25));
for (int i = 0; i < 200; i++) {
SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:image];
imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), image.size.width * .25, image.size.height * .25);
CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = path;
imageView.layer.mask = shape;
[self.view addSubview:imageView];
[imageView release];
}
CGPathRelease(path);
Z powyższego kodu, imageView
jest bardzo laggy. Jednak reaguje natychmiast gdybym maskować go ręcznie w bitmap context
:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"3.0-Pad-Classic0.jpg" ofType:nil]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25));
for (int i = 0; i < 200; i++) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width * .25, image.size.height * .25), NO, [[UIScreen mainScreen]scale]);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddPath(ctx, path);
CGContextClip(ctx);
[image drawInRect:CGRectMake(-(image.size.width * .25), -(image.size.height * .25), image.size.width, image.size.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:finalImage];
imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), finalImage.size.width, finalImage.size.height);
[self.view addSubview:imageView];
[imageView release];
}
CGPathRelease(path);
Nawiasem mówiąc, tutaj jest kod do SLTUIImageView
, to tylko prosty podklasa UIImageView
że reaguje na dotknięcia (dla każdego, kto zastanawiałem) :
-(id)initWithImage:(UIImage *)image{
self = [super initWithImage:image];
if (self) {
self.userInteractionEnabled = YES;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.superview bringSubviewToFront:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
self.center = [touch locationInView:self.superview];
}
Czy to możliwe, aby w jakiś sposób zoptymalizować sposób CAShapeLayer
jest maskowanie UIImageView
tak, że poprawia się wydajność? Próbowałem dowiedzieć się, gdzie butelka-dekolt używa Time Profiler
w , ale nie mogę powiedzieć dokładnie, co jest przyczyną.
Próbowałem ustawić shouldRasterize
na YES
zarówno na layer
, jak i na layer.mask
, ale żaden z nich nie ma żadnego efektu. Nie jestem pewien, co robić.
Edit:
mam zrobić więcej badań, a okaże się, że jeśli mogę użyć tylko regularne CALayer
zamaskować inny CALayer (layer.mask = someOtherLayer)
mam te same problemy z wydajnością. Wydaje się, że problem nie jest specyficzny dla CAShapeLayer
- ponieważ jest specyficzny dla właściwości mask
z CALayer
.
Edit 2:
Więc po zapoznaniu się więcej o użyciu Core Animation tool
w Instruments
, dowiedziałem się, że widok jest renderowany poza ekranem każdym razem porusza. Ustawienie do , gdy dotyk zaczyna się i wyłączenie po zakończeniu dotyku powoduje, że widok pozostaje zielony (zachowując pamięć podręczną) w instruments
, ale wydajność jest nadal straszna. Sądzę, że tak jest, ponieważ nawet jeśli widok jest buforowany, jeśli nie jest nieprzezroczysty, to nadal musi być ponownie renderowany przy każdej klatce.
Jedną z rzeczy, które należy podkreślić jest to, że jeśli tylko kilka widoków jest zamaskowanych (powiedzmy nawet około dziesięciu), wydajność jest całkiem dobra. Jednak po zwiększeniu tej wartości do 100 or more
wydajność spada. Wyobrażam sobie, że dzieje się tak dlatego, że gdy ktoś porusza się nad innymi, wszystkie muszą zostać ponownie wyrenderowane.
Mój wniosek jest taki, Mam jedną z dwóch opcji.
Po pierwsze, musi istnieć jakiś sposób, aby trwale zamaskować widok (wyrenderować go raz i nazwać dobrze).Wiem, że można to zrobić poprzez graficzną lub bitową ścieżkę kontekstu, jak pokazuję w moim przykładowym kodzie, ale gdy warstwa maskuje jej widok, dzieje się to natychmiast. Kiedy robię to w kontekście bitmapowym, jak pokazano, jest dość powolny (ponieważ prawie nie da się nawet porównać jego wolniej).
Po drugie, musi być jakiś sposób, aby wykonać to za pomocą ścieżki kontekstowej bitmapy. Jeśli istnieje ekspert od maskowania obrazów lub widoków, ich pomoc byłaby bardzo doceniana.
Najlepszym rozwiązaniem jest skompresowanie 200 obrazów dodanych do pętli for w jednym dużym obrazie przy użyciu kodu podobnego do tego podanego tutaj: http://stackoverflow.com/questions/4334233/how-to-capture- uiview-to-uiimage-bez-utraty-jakości-na-retina-wystawie Możesz także spróbować użyć instrumentów i użyć profilera czasu, aby sprawdzić, gdzie jest wąskie gardło twojego kodu. –
Czy próbowałeś renderować obrazy z wyprzedzeniem do ich ostatecznego potrzebnego stanu, zanim użyjesz ich w aplikacji, aby aplikacja nie musiała wykonywać całego tego przetwarzania grafiki, zwłaszcza, że masz tak wiele obrazów, aby wykonać to przetwarzanie i są poruszane, więc ciągle muszą ponownie renderować je wszystkie. Nie jestem pewien, czy jest to realna opcja dla Ciebie i czy próbowałeś ją jeszcze. Trudno jest też zrozumieć, co próbujesz zrobić z obrazami bez obrazu, w tym przypadku ważne jest, aby dołączyć zrzut ekranu. –
Wynika to z renderowania poza ekranem, jak już wskazałeś. Nie sądzę, że jest gdzieś daleko, aby zoptymalizować wydajność warstwy, ale jak już zauważyłeś, możesz zoptymalizować proces. Jeszcze jeden krok może być podklasą UIView zastąpić drawRect, aby osiągnąć to, czego potrzebujesz. Funkcja drawRect jest wywoływana tylko wtedy, gdy widok jest oznaczony jako brudny. Nie podklasy UIImageView bacuse drawRect nigdy nie jest wywoływany. – Andrea