【问题标题】:Create layer mask with custom-shaped hole使用自定义形状的孔创建图层蒙版
【发布时间】:2011-08-08 22:28:27
【问题描述】:

我花了太多时间试图解决这个问题,但根本找不到可行的解决方案。

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

一个示例可能是缩放效果,您可以在图像周围移动这个“洞”。在洞内你可以正常看到图像,而在洞外则被半透明层覆盖。注意:我在 cocos2d 层中实现这一点,其中图像由 CCSprite 表示。不过,如果没有使用 cocos,应该没关系。

问题: 我尝试使用 CAShapeLayer 和位图作为掩码,但没有任何效果(参见下面的代码 sn-ps)。使用 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 );

【问题讨论】:

标签: iphone ios cocos2d-iphone


【解决方案1】:

在学习了其他核心图形技术后,我又回到了这个问题。 该解决方案最接近上面的多层蒙版示例。 但是,您需要将两条路径组合成一个方向相反的 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)];

然后,将相反方向 (CCW) 的外部区域添加到同一路径。注意: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) 上的蒙版。不需要“外层”。

【讨论】:

  • 使用appendPath: 可以更轻松地组合路径 - 使用整个框架的矩形创建单个路径,然后附加代表各个孔的路径。将填充规则更改为偶数奇数和嘿presto。
  • 好点。但这是否需要以同样的方式创建一条额外的路径?即,您将执行相同的代码来添加点,但在另一条路径上,然后添加该路径...与。直接将其添加到原始路径中。不过,您的方法可能更具可读性。
  • 我想我应该在多个掩码示例中对这个问题发表评论。
  • 感谢使用这种技术来绘制存储在 Spatialite 数据库中的一些复杂的 GIS 多边形(包含孔)。使用特定查询函数提取几何图形:ExteriorRing(Geometry)、InteriorRingN(Geometry, N) 和 ST_NRings(Geometry)
猜你喜欢
  • 1970-01-01
  • 2014-11-09
  • 1970-01-01
  • 1970-01-01
  • 2017-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-13
相关资源
最近更新 更多