在CALayer上进行过滤,但形状是(不一定是不同的)矩形的并集的形状 [英] Filter on CALayer except for a shape which is an union of (non necessarily distinct) rectangles

查看:118
本文介绍了在CALayer上进行过滤,但形状是(不一定是不同的)矩形的并集的形状的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将CIFilter应用于CALayer,除了是矩形联合的区域之外. 我发布了我无法正常工作的代码,它的工作恰好相反,即过滤器仅在矩形中应用,而不在外部!

I want to apply a CIFilter to a CALayer except for an area which is an union of rectangles. I post my not working code, it does exactly the opposite, i.e. the filter is applied only in the rectangles and not outside!

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()
}

然后是Internet上的以下代码,因为与UIBezierPath不同,NSBezierPath不具有CGPath属性.

and then the following code from the Internet since NSBezierPath does not have the attribute CGPath, unlike 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
}

推荐答案

AFAIK,您无法将图层蒙版到路径的反面.

AFAIK, you can't mask a layer to the inverse of a path.

一些观察结果:

  1. 如果您只是想剔除整个视图中的路径,则可以通过创建包含整个CGRect以及各种内部路径的路径的技巧来做到这一点,并利用even/奇怪的缠绕/填充规则,但是如果您的内部路径相互重叠,该规则将无效.

  1. If you were just trying to knock out the paths inside the whole view, you could do that with the trick of creating a path that consists of the whole CGRect plus the various interior paths and leverage the even/odd winding/fill rule, but that won't work if your interior paths overlap with each other.

您可以将图像蒙版到路径的反面(通过创建单独的图像蒙版"),但不适用于动态CALayer蒙版.它用于掩盖NSImage.因此,如果可以将快照用于视图的过滤部分,那就可以了.

You can mask images to the inverse of a path (by creating a separate "image mask"), but that won't work for dynamic CALayer masking. It's used for masking a NSImage. So, if you were OK using a snapshot for the filtered part of the view, that's an option.

有关使用图像蒙版的示例,请参见下面的代码段.

See code snippet below for example of using image masks.

另一种方法是将过滤器应用于整个视图,对其进行快照,然后将该快照置于所讨论的视图下方,然后将顶层视图遮盖到内部路径.实际上,将未过滤的视图遮盖到内部路径,在其下方显示视图的过滤快照.

Another approach is to apply your filter to the whole view, snapshot it, put that snapshot underneath the view in question and then mask the top level view to your interior paths. In effect, mask the un-filtered view to your interior paths, revealing a filtered snapshot of your view below it.

然而,方法是创建一条表示所有内部路径的并集轮廓的路径.如果路径很简单(例如,非旋转的矩形),则非常简单.如果路径很复杂(一些旋转的路径,一些非矩形的路径等),则会变得很毛茸茸.但是,琐碎的场景还算不错.无论如何,如果您这样做了,那么您可以退回到那个奇数的花样.

Yet approach would be to create a path representing the outline of the union of all of your interior paths. If the paths are simple (e.g. non-rotated rectangles) this is pretty easy. If the paths are complex (some rotated, some non-rectangular paths, etc.), this gets hairy. But the trivial scenario isn't too bad. Anyway, if you do that, then you can fall back to that even-odd trick.

我对这些方法中的任何一种都不完全满意,但是我看不到任何其他方法可以完成您所寻找的目标.希望有人会提出一些更好的方法来解决这个问题.

I'm not wholly satisfied with any of these approaches, but I don't see any other way to accomplish what you're looking for. Hopefully someone will suggest some better ways to tackle this.

要扩展选项2(使用通过绘制一些路径(可能会重叠)而创建的图像蒙版),在Swift 3中,您可以执行以下操作:

To expand on option 2 (using an image mask created by drawing a few paths, possibly overlapping), in Swift 3 you can do something like:

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

结果是:

这会掩盖绘制的路径.

这篇关于在CALayer上进行过滤,但形状是(不一定是不同的)矩形的并集的形状的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆