使用自定义形状创建图层蒙版,蒙版中包含一个洞。

16

我已经花费了太多时间来尝试解决这个问题,但是还是找不到可行的解决方案。

情况: 1. 在手机上显示了一个“某物”的图片。 2. 在图像顶部放置了一个半透明的(例如蓝色)层,完全覆盖了它。 3. 在此层中存在一个“孔”,该部分完全透明且可移动。

一个例子可能是缩放效果,在其中您可以在图像周围移动这个“孔”。在孔内部,您可以正常看到图像,而在外面则被半透明层覆盖。注意:我正在一个cocos2d层中实现此功能,其中图像由CCSprite表示。如果没有使用cocos,那么这不应该有影响。

问题: 我尝试使用CAShapeLayer和位图作为掩码,但没有任何作用(请参见下面的代码片段)。对于CAShapeLayer,我创建了一个UIBezierPath以获取“孔”,并将其应用于有颜色的层。然而,只有孔显示颜色,而其余部分则透明。使用图像时,掩码根本没有起作用(我不知道为什么)。我甚至尝试过使用掩码来进行掩蔽,以查看是否有效。我还尝试了一些交换颜色的方法……从白色到黑色再到透明,用于填充和背景。

一个简单的解决方案(如果存在),就是反转UIBezierPath的区域。我也尝试过使用路径进行剪辑,但没有成功。

我希望这只是一个简单愚蠢的问题,而我只是忽视了它。也许你们中的其中一个会看到这个问题。我还不关心移动部分。我需要先让实际掩码起作用。示例代码忽略了iPhone SDK和openGL之间的y轴差异。

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];

多层蒙版示例:

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];

图像遮罩示例:

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 );

请查看以下类似的问题和答案是否有所帮助:在UIView中裁剪透明孔 - dichen
1个回答

13

在学习了其他核心图形技术后,我回来看了这个问题。 解决方案与上面的多层蒙版示例最接近。 但是,您不需要创建内部和外部层,而是需要将两个路径以相反方向组合成单个UIBezierPath。

因此,例如,创建一个要裁剪的内部区域的路径(CW)。注意:x、y、w、h是指“孔”的原点和大小。

      [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)];

接着,以相反的方向(逆时针)将外部矩形区域添加到相同的路径中。注意:x、y、w、h是指外部矩形的原点和大小。

      [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)];

这个路径然后被应用到一个图层(maskLayer),该图层被用作最终图层(colorLayer)上的蒙版。 "outerLayer"不需要。


1
通过使用 appendPath:,合并路径可以变得更加容易-创建一个完整框架矩形的单一路径,然后附加表示各个孔的路径。将填充规则更改为even odd,完成了。 - jrturton
好点子。但这是否需要以同样的方式创建另一条路径呢?也就是说,你将会编写相同的代码来添加点,但在另一条路径上,然后再添加那条路径...与直接将其添加到原始路径相比,你的方法可能会更易读。 - GtotheB
我认为在多重掩码示例中,我应该对问题进行评论。 - jrturton
感谢使用这种技术来绘制存储在Spatialite数据库中的一些包含孔的复杂GIS多边形。使用特定的查询函数提取几何图形:ExteriorRing(Geometry),InteriorRingN(Geometry,N)和ST_NRings(Geometry)。 - Wayne Shelley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接