2013-06-11 10 views
23

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.

+0

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. –

+0

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. –

+1

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

Odpowiedz

1

Dostałeś się dość daleko i wydaje mi się, że prawie rozwiązanie. To, co chciałbym zrobić, to po prostu przedłużenie tego, co już wypróbowałeś. Ponieważ mówisz, że wiele z tych warstw "kończy się" w końcowych pozycjach, które pozostają stałe w stosunku do innych warstw i maski ... Po prostu renderuj wszystkie te "ukończone" warstwy do pojedynczego kontekstu bitmapowego. W ten sposób, za każdym razem, gdy napiszesz warstwę do tego pojedynczego kontekstu, będziesz miał mniej martwić się warstwą, która spowalnia proces animacji/renderowania.

Powiązane problemy