在 CALayer 上过滤,除了形状是(不一定不同的)矩形的并集
Filter on CALayer except for a shape which is an union of (non necessarily distinct) rectangles
我想将 CIFilter
应用到 CALayer
,除了一个矩形联合区域。
我 post 我的代码不工作,它的作用恰恰相反,即过滤器仅应用于矩形而不是外部!
func refreshTheFrameWithEffect() {
self.layer!.masksToBounds = true
self.layerUsesCoreImageFilters = true
self.layer!.needsDisplayOnBoundsChange = true
let filter = CIFilter(name: "CICircleSplashDistortion")
filter!.setDefaults()
self.layer!.backgroundFilters = [filter!]
var excludedRects: [CGRect] = [] //INITIALISE THEM HOW YOU PREFER
let maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds
self.layer!.fillMode = kCAFillRuleEvenOdd
var maskPath = NSBezierPath()
for rect in excludedRects {
maskPath.append(NSBezierPath(rect: rect))
}
maskLayer.path = maskPath.CGPath
self.layer!.mask = maskLayer
self.layer!.needsDisplay()
}
然后下面的代码来自互联网,因为 NSBezierPath 没有属性 CGPath,不像 UIBezierPath。
public var CGPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveToBezierPathElement: path.move(to: CGPoint(x: points[0].x, y: points[0].y) )
case .lineToBezierPathElement: path.addLine(to: CGPoint(x: points[0].x, y: points[0].y) )
case .curveToBezierPathElement: path.addCurve( to: CGPoint(x: points[2].x, y: points[2].y),
control1: CGPoint(x: points[0].x, y: points[0].y),
control2: CGPoint(x: points[1].x, y: points[1].y) )
case .closePathBezierPathElement: path.closeSubpath()
}
}
return path
}
据我所知,您不能将图层遮盖到路径的反向。
一些观察:
如果您只是想在整个视图中剔除路径,您可以使用创建一个由整个 CGRect
加上各种内部结构组成的路径的技巧来做到这一点路径并利用 even/odd winding/fill 规则,但如果您的内部路径相互重叠,那将不起作用。
您可以将图像屏蔽为路径的反向(通过创建单独的“图像屏蔽”),但这不适用于动态 CALayer
屏蔽。它用于屏蔽 NSImage
。因此,如果您可以为视图的过滤部分使用快照,那是一个选项。
有关使用图像遮罩的示例,请参见下面的代码片段。
另一种方法是将过滤器应用于整个视图,对其进行快照,将该快照放在有问题的视图下方,然后将顶级视图屏蔽到您的内部路径。实际上,将未过滤的视图屏蔽到您的内部路径,在其下方显示您的视图的过滤快照。
然而,方法是创建一条路径,代表所有内部路径的并集轮廓。如果路径很简单(例如非旋转矩形),这很容易。如果路径很复杂(有些是旋转的,有些是非矩形路径等),这会变得很棘手。但这个微不足道的场景还算不错。不管怎样,如果你这样做了,那么你就可以退回到那个偶数把戏了。
我对这些方法中的任何一个都不完全满意,但我看不到任何其他方法来完成您正在寻找的东西。希望有人会提出一些更好的方法来解决这个问题。
要扩展选项 2(使用通过绘制一些可能重叠的路径创建的图像蒙版),在 Swift 3 中,您可以执行以下操作:
private func maskImageWithPaths(image: NSImage, size: CGSize) -> NSImage {
// define parameters for two overlapping paths
let center1 = CGPoint(x: size.width * 0.40, y: size.height / 2)
let radius1 = size.width * 0.20
let center2 = CGPoint(x: size.width * 0.60 , y: size.height / 2)
let radius2 = size.width * 0.20
// create these two overlapping paths
let path = CGMutablePath()
path.move(to: center1)
path.addArc(center: center1, radius: radius1, startAngle: -.pi / 2, endAngle: .pi * 3 / 2, clockwise: false)
path.move(to: center2)
path.addArc(center: center2, radius: radius2, startAngle: -.pi / 2, endAngle: .pi * 3 / 2, clockwise: false)
// create image from these paths
let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bitmapFormat: .alphaFirst, bytesPerRow: 4 * Int(size.width), bitsPerPixel: 32)!
let context = NSGraphicsContext(bitmapImageRep: imageRep)!
context.cgContext.addPath(path)
context.cgContext.setFillColor(NSColor.blue.cgColor)
context.cgContext.fillPath()
let maskImage = context.cgContext.makeImage()!
let mask = CGImage(maskWidth: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: 4 * Int(size.width), provider: maskImage.dataProvider!, decode: nil, shouldInterpolate: true)!
let finalImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)!.masking(mask)!
return NSImage(cgImage: finalImage, size: size)
}
产生:
这会掩盖绘制的路径。
我想将 CIFilter
应用到 CALayer
,除了一个矩形联合区域。
我 post 我的代码不工作,它的作用恰恰相反,即过滤器仅应用于矩形而不是外部!
func refreshTheFrameWithEffect() {
self.layer!.masksToBounds = true
self.layerUsesCoreImageFilters = true
self.layer!.needsDisplayOnBoundsChange = true
let filter = CIFilter(name: "CICircleSplashDistortion")
filter!.setDefaults()
self.layer!.backgroundFilters = [filter!]
var excludedRects: [CGRect] = [] //INITIALISE THEM HOW YOU PREFER
let maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds
self.layer!.fillMode = kCAFillRuleEvenOdd
var maskPath = NSBezierPath()
for rect in excludedRects {
maskPath.append(NSBezierPath(rect: rect))
}
maskLayer.path = maskPath.CGPath
self.layer!.mask = maskLayer
self.layer!.needsDisplay()
}
然后下面的代码来自互联网,因为 NSBezierPath 没有属性 CGPath,不像 UIBezierPath。
public var CGPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount {
let type = self.element(at: i, associatedPoints: &points)
switch type {
case .moveToBezierPathElement: path.move(to: CGPoint(x: points[0].x, y: points[0].y) )
case .lineToBezierPathElement: path.addLine(to: CGPoint(x: points[0].x, y: points[0].y) )
case .curveToBezierPathElement: path.addCurve( to: CGPoint(x: points[2].x, y: points[2].y),
control1: CGPoint(x: points[0].x, y: points[0].y),
control2: CGPoint(x: points[1].x, y: points[1].y) )
case .closePathBezierPathElement: path.closeSubpath()
}
}
return path
}
据我所知,您不能将图层遮盖到路径的反向。
一些观察:
如果您只是想在整个视图中剔除路径,您可以使用创建一个由整个
CGRect
加上各种内部结构组成的路径的技巧来做到这一点路径并利用 even/odd winding/fill 规则,但如果您的内部路径相互重叠,那将不起作用。您可以将图像屏蔽为路径的反向(通过创建单独的“图像屏蔽”),但这不适用于动态
CALayer
屏蔽。它用于屏蔽NSImage
。因此,如果您可以为视图的过滤部分使用快照,那是一个选项。有关使用图像遮罩的示例,请参见下面的代码片段。
另一种方法是将过滤器应用于整个视图,对其进行快照,将该快照放在有问题的视图下方,然后将顶级视图屏蔽到您的内部路径。实际上,将未过滤的视图屏蔽到您的内部路径,在其下方显示您的视图的过滤快照。
然而,方法是创建一条路径,代表所有内部路径的并集轮廓。如果路径很简单(例如非旋转矩形),这很容易。如果路径很复杂(有些是旋转的,有些是非矩形路径等),这会变得很棘手。但这个微不足道的场景还算不错。不管怎样,如果你这样做了,那么你就可以退回到那个偶数把戏了。
我对这些方法中的任何一个都不完全满意,但我看不到任何其他方法来完成您正在寻找的东西。希望有人会提出一些更好的方法来解决这个问题。
要扩展选项 2(使用通过绘制一些可能重叠的路径创建的图像蒙版),在 Swift 3 中,您可以执行以下操作:
private func maskImageWithPaths(image: NSImage, size: CGSize) -> NSImage {
// define parameters for two overlapping paths
let center1 = CGPoint(x: size.width * 0.40, y: size.height / 2)
let radius1 = size.width * 0.20
let center2 = CGPoint(x: size.width * 0.60 , y: size.height / 2)
let radius2 = size.width * 0.20
// create these two overlapping paths
let path = CGMutablePath()
path.move(to: center1)
path.addArc(center: center1, radius: radius1, startAngle: -.pi / 2, endAngle: .pi * 3 / 2, clockwise: false)
path.move(to: center2)
path.addArc(center: center2, radius: radius2, startAngle: -.pi / 2, endAngle: .pi * 3 / 2, clockwise: false)
// create image from these paths
let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bitmapFormat: .alphaFirst, bytesPerRow: 4 * Int(size.width), bitsPerPixel: 32)!
let context = NSGraphicsContext(bitmapImageRep: imageRep)!
context.cgContext.addPath(path)
context.cgContext.setFillColor(NSColor.blue.cgColor)
context.cgContext.fillPath()
let maskImage = context.cgContext.makeImage()!
let mask = CGImage(maskWidth: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: 4 * Int(size.width), provider: maskImage.dataProvider!, decode: nil, shouldInterpolate: true)!
let finalImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)!.masking(mask)!
return NSImage(cgImage: finalImage, size: size)
}
产生:
这会掩盖绘制的路径。