iphone - Crear máscara de capa con agujero en forma personalizada
ios cocos2d-iphone (1)
He pasado demasiado tiempo tratando de resolver esto y simplemente no puedo encontrar una solución viable.
Situación: 1. Se muestra una imagen de "algo" en el teléfono. 2. Una capa semitransparente (por ejemplo, azul) se coloca sobre la imagen, cubriéndola completamente. 3. Existe un ''agujero'' en esta capa donde esa parte de la capa es completamente transparente y se puede mover.
Un ejemplo podría ser un efecto de zoom donde mueves este ''agujero'' alrededor de la imagen. Dentro del agujero se puede ver la imagen normalmente, mientras que afuera está cubierta por la capa semitransparente. NOTA: Estoy implementando esto en una capa cocos2d, donde la imagen está representada por un CCSprite. Sin embargo, no debería importar si no se usan cocos.
Problema: he intentado usar CAShapeLayer y los mapas de bits como máscaras, pero nada funciona (consulte los fragmentos de código a continuación). Con el CAShapeLayer, creo un UIBezierPath para el ''agujero'' y lo aplico a la capa de color. Sin embargo, solo el agujero muestra el color, mientras que el resto es transparente. Con una imagen, la máscara simplemente no funciona (no tengo idea de por qué). Incluso he intentado enmascarar máscaras para ver si eso funcionaría. También he intentado cambiar los colores ... de blanco a negro para borrar el relleno y el fondo.
Una solución simple, si existiera, sería invertir el área de UIBezierPath. También traté de recortar, usando el camino ... pero no tuve suerte.
Espero que sea algo simple-estúpido que simplemente esté pasando por alto. Quizás uno de ustedes vea esto. La parte móvil no me preocupa todavía. Necesito conseguir que la máscara real funcione primero. El código de muestra está ignorando las diferencias en el eje y entre iPhone SDK y openGL.
Ejemplo de CAShapeLayer:
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];
Ejemplo de máscara de capas múltiples:
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];
Ejemplo de máscara de imagen:
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 );
Volví a esto más tarde después de aprender otras técnicas de gráficos básicos. La solución es la más cercana al ejemplo de máscara de múltiples capas anterior. Sin embargo, en lugar de crear una capa interna y externa, debe combinar dos rutas en una única UIBezierPath en direcciones opuestas.
Entonces, por ejemplo, cree una ruta de acceso del área interna que se va a recortar (CW) NOTA: x, y, w, h se refieren al origen y tamaño del "agujero".
[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)];
Luego, agregue al mismo camino el área exterior en la dirección opuesta (CCW). NOTA: x, y, w, h se refieren al origen y tamaño de la rectificación externa.
[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)];
Esta ruta se aplica a una capa (maskLayer), que se utiliza como máscara en la capa final (colorLayer). El "outerLayer" no es necesario.