2011-04-19 10 views
16

Spędziłem zbyt wiele czasu, próbując to zrozumieć i po prostu nie mogę znaleźć praktycznego rozwiązania.Utwórz maskę warstwy z niestandardowym otworem

Sytuacja: 1. Obraz "czegoś" jest wyświetlany na telefonie. 2. Półprzezroczysta (np. Niebieska) warstwa jest umieszczona na górze obrazu, całkowicie ją zakrywając. 3. "Otwór" w tej warstwie istnieje tam, gdzie ta część warstwy jest całkowicie przezroczysta i ruchoma.

Przykładem może być efekt powiększenia, w którym przesuwa się tę "dziurę" wokół obrazu. Wewnątrz otworu widać normalnie obraz, a na zewnątrz pokrywa go półprzezroczysta warstwa. UWAGA: Wdrażam to w warstwie cocos2d, gdzie obraz jest reprezentowany przez CCSprite. Nie powinno to jednak mieć znaczenia, jeśli nie używa się cocos.

Problem: Próbowałem za pomocą CAShapeLayer i bitmapy jako maski, ale nic nie działa (patrz fragmenty kodu poniżej). Za pomocą CAShapeLayer tworzę ścieżkę UIBezierPath dla "otworu" i stosuję ją do kolorowej warstwy. Jednak tylko dziura pokazuje kolor, podczas gdy reszta jest przezroczysta. Z obrazem maska ​​po prostu nie działa (nie mam pojęcia dlaczego). Próbowałem nawet maski maskujące, żeby sprawdzić, czy to zadziała. Próbowałem również zamieniać kolory wokół ... z białego na czarny, aby wyczyścić wypełnienie i tło.

Prostym rozwiązaniem, gdyby istniało, byłoby odwrócenie obszaru UIBezierPath. Próbowałem również wycinania, używając ścieżki ... ale bez powodzenia.

Mam nadzieję, że to jest coś prostego - głupiego, że po prostu przeoczyłem. Być może któryś z was to zobaczy. Część ruchoma, z którą nie jestem związany. Najpierw muszę sprawić, żeby maska ​​zadziałała. Przykładowy kod ignoruje różnice osi Y między iPhone SDK i OpenGL.

CAShapeLayer Przykład:

CGSize winSize = [[CCDirector sharedDirector] winSize]; 
UIImage* img = [UIImage imageNamed:@"zebra.png"]; 
CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"]; 
spr.position = ccp(winSize.width/2, winSize.width/2); 
[self addSprite:spr]; 

UIBezierPath* path = [UIBezierPath bezierPathWithRect:rectHole]; 
CAShapeLayer* maskLayer = [CAShapeLayer layer]; 
maskLayer.bounds = [spr boundingBox]; 
maskLayer.position = spr.position; 
maskLayer.fillColor = [UIColor whiteColor].CGColor; 
maskLayer.backgroundColor = [UIColor clearColor].CGColor; 
maskLayer.path = path.CGPath; 

CALayer* colorLayer = [CALayer layer]; 
colorLayer.bounds = [spr boundingBox]; 
colorLayer.position = maskLayer.position; 
[colorLayer setMask:maskLayer]; 

[[[[CCDirector sharedDirector] openGLView] layer] addSublayer:colorLayer]; 

wielowarstwowy Maska Przykład:

CGSize winSize = [[CCDirector sharedDirector] winSize]; 
UIImage* img = [UIImage imageNamed:@"zebra.png"]; 
CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"]; 
spr.position = ccp(winSize.width/2, winSize.width/2); 
[self addSprite:spr]; 

UIBezierPath* path = [UIBezierPath bezierPathWithRect:rectHole]; 
CAShapeLayer* maskLayer = [CAShapeLayer layer]; 
maskLayer.bounds = [spr boundingBox]; 
maskLayer.position = spr.position; 
maskLayer.fillColor = [UIColor whiteColor].CGColor; 
maskLayer.backgroundColor = [UIColor clearColor].CGColor; 
maskLayer.path = path.CGPath; 

UIBezierPath* pathOuter = [UIBezierPath bezierPathWithRect:img.frame]; 
CAShapeLayer* outerLayer = [CAShapeLayer layer]; 
outerLayer.bounds = [spr boundingBox]; 
outerLayer.position = spr.position; 
outerLayer.fillColor = [UIColor blackColor].CGColor; 
outerLayer.backgroundColor = [UIColor whiteColor].CGColor; 
outerLayer = pathOuter.CGPath; 
[outerLayer setMask:maskLayer]; 

CALayer* colorLayer = [CALayer layer]; 
colorLayer.bounds = [spr boundingBox]; 
colorLayer.position = outerLayer.position; 
[colorLayer setMask:outerLayer]; 

[[[[CCDirector sharedDirector] openGLView] layer] addSublayer:colorLayer]; 

Obraz Maska Przykład:

CGSize winSize = [[CCDirector sharedDirector] winSize]; 
UIImage* img = [UIImage imageNamed:@"zebra.png"]; 
CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"]; 
spr.position = ccp(winSize.width/2, winSize.width/2); 
[self addSprite:spr]; 

CGRect r = [spr boundingBox]; 
CGSize sz = CGSizeMake(r.size.width, r.size.height); 
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); 
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 0, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaNone); 
CGColorSpaceRelease(colorSpace); 
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 
CGContextFillRect(context, r); 
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor); 
CGContextFillRect(context, rectHole); 
CGImageRef ref = CGBitmapContextCreateImage(context); 
CGContextRelease(context); 

CALayer* maskLayer = [CALayer layer]; 
maskLayer.bounds = [spr boundingBox]; 
maskLayer.position = spr.position; 
[maskLayer setContents:(id)ref]; 

CALayer* colorLayer = [CALayer layer]; 
colorLayer.bounds = [spr boundingBox]; 
colorLayer.position = maskLayer.position; 
[colorLayer setMask:maskLayer]; 

[[[[CCDirector sharedDirector] openGLView] layer] addSublayer:colorLayer]; 
CGImageRelease(ref); 
+0

Zobacz, czy to podobne pytanie i odpowiedź help: [Wytnij otwór w przejrzysty UIView] [1] [1]: http://stackoverflow.com/questions/9711248/cut-transparent-hole- in-uiview/30994705 # 30994705 – dichen

Odpowiedz

13

Wróciłem do tego później po nauce innych podstawowych technik graficznych. Rozwiązanie jest najbliższe przykładowi maski wielowarstwowej. Jednak zamiast tworzyć warstwę wewnętrzną i zewnętrzną, należy połączyć dwie ścieżki w jedną ścieżkę UIBezierPath w przeciwnych kierunkach.

Na przykład utwórz ścieżkę obszaru wewnętrznego, który ma zostać przycięty (CW). UWAGA: x, y, w, h odnoszą się do początku i wielkości "dziury".

 [path moveToPoint:ccp(x,y)]; 
     [path addLineToPoint:ccp(x+w,y)]; 
     [path addLineToPoint:ccp(x+w,y+h)]; 
     [path addLineToPoint:ccp(x,y+h)]; 
     [path addLineToPoint:ccp(x,y)]; 

Następnie dodać do tej samej ścieżki obszar zewnętrzny w przeciwnym kierunku (CCW). UWAGA: x, y, w, h odnoszą się do początku i wielkości zewnętrznego recta.

 [path moveToPoint:ccp(x,y)]; 
     [path addLineToPoint:ccp(x,y+h)]; 
     [path addLineToPoint:ccp(x+w,y+h)]; 
     [path addLineToPoint:ccp(x+w,y)]; 
     [path addLineToPoint:ccp(x,y)]; 

Ten sposób jest następnie nakładana na warstwę (maskLayer), która jest wykorzystywana jako maska ​​na końcową warstwę (colorLayer). "Zewnętrzna warstwa" nie jest potrzebna.

+1

Łączenie ścieżek może być znacznie łatwiejsze dzięki użyciu 'appendPath:' - utwórz pojedynczą ścieżkę z rect całej ramki, a następnie dodaj ścieżki reprezentujące poszczególne otwory. Zmień regułę wypełnienia, aby była nawet nieparzysta i hej presto. – jrturton

+0

Dobra uwaga. Ale czy nie oznaczałoby to stworzenia dodatkowej ścieżki w ten sam sposób? Oznacza to, że będziesz robił ten sam kod, aby dodawać punkty, ale na innej ścieżce, a następnie dodając tę ​​ścieżkę ... vs. dodawanie bezpośrednio do oryginalnej ścieżki. Twoje podejście będzie jednak bardziej czytelne. – GtotheB

+0

Myślę, że powinienem był komentować to pytanie, na przykładzie wielu masek. – jrturton

Powiązane problemy