如何组合/嵌套CALayer蒙版?

huangapple go评论129阅读模式
英文:

How to combine / nest CALayer masks?

问题

以下是翻译好的部分:

"Is it possible use nested CALayer mask to create one combined mask? My goal is to create a complex shapes by combining / masking other complex shapes and applying a gradient to the result.

是否可以使用嵌套的CALayer蒙版来创建一个组合蒙版?我的目标是通过组合/蒙版其他复杂形状并将渐变应用于结果来创建复杂的形状。

For example one CAShapeLayer my hold a path which draws a unicorn head and a second CAShapeLayer my hold a path which draws a flower. By applying the flower layer as mask to the unicorn layer, I get a "flowerly unicorn". In then next step I could than apply this complex shape to a CAGradientLayer to get a "flowerly unicorn rainbow".

例如,一个CAShapeLayer可以包含绘制独角兽头部的路径,第二个CAShapeLayer可以包含绘制花朵的路径。通过将花朵图层用作独角兽图层的蒙版,我可以得到一个"花朵独角兽"。然后,在下一步中,我可以将这个复杂形状应用到CAGradientLayer上,以获得一个"花朵独角兽彩虹"。

However, when applying the unicorn layer (with the flower mask) as mask for the gradient, the result is a unicorn rainbow. The flower mask already applied to the unicorn is skipped.

然而,当将独角兽图层(带有花朵蒙版)用作渐变的蒙版时,结果是一个独角兽彩虹。已经应用到独角兽的花朵蒙版被跳过。

Is there any other way to combine / nest layer mask to create such shapes?

是否有其他方法来组合/嵌套图层蒙版以创建这样的形状?

The following code illustrates the problem using some simpler shapes:

以下的代码示例使用一些简单的形状来说明问题:

let rectLayer = CAShapeLayer()
rectLayer = // ... a simple rect shape

let circleLayer = CAShapeLayer()
circleLayer = // ... a circle shape, half overlapping the rect

// Apply rect as mask to create half circle
circleLayer.mask = rectLayer

// Some gradient
let gradientLayer = CAGradientLayer()
gradientLayer = // ... set up gradient with some colors

// Apply masked circle as mask to get a half circle with gradient
gradientLayer.mask = circleLayer

Now I would assume to get a half circle shape with a gradient. However, I get a circle with a gradient instead. It seem that when applying the circleLayer as mask to the gradientLayer the rect mask is not considered / applied...

现在我会认为会得到一个带有渐变的半圆形状。然而,我得到的却是一个带有渐变的圆形。似乎在将circleLayer用作gradientLayer的蒙版时,矩形蒙版没有被考虑/应用...

Am I doing something wrong? Is there another way to solve this? Masking and combining masks is quite a common task in many different applications. Thus I wonder if such a powerful framework as CA has a way of doing this.

我是否做错了什么?是否有其他解决方法?在许多不同的应用中,蒙版和组合蒙版是相当常见的任务。因此,我想知道像CA这样强大的框架是否有一种方法来实现这一点。"

英文:

Is it possible use nested CALayer mask to create one combined mask? My goal is to create a complex shapes by combining / masking other complex shapes and applying a gradient to the result.

For example one CAShapeLayer my hold a path which draws a unicorn head and a second CAShapeLayer my hold a path which draws a flower. By applying the flower layer as mask to the unicorn layer, I get a "flowerly unicorn". In then next step I could than apply this complex shape to a CAGradientLayer to get a "flowerly unicorn rainbow".

However, when applying the unicorn layer (with the flower mask) as mask for the gradient, the result is a unicorn rainbow. The flower mask already applied to the unicorn is skipped.

Is there any other way to combine / nest layer mask to create such shapes?

<hr>

The following code illustrates the problem using some simpler shapes:

let rectLayer = CAShapeLayer()
rectLayer = // ... a simple rect shape

let circleLayer = CAShapeLayer()
circleLayer = // ... a circle shape, half overlapping the rect

// Apply rect as mask to create half circle
circleLayer.mask = rectLayer

// Some gradient
let gradientLayer = CAGradientLayer()
gradientLayer = // ... set up gradient with some colors

// Apply masked circle as mask to get a half circle with gradient
gradientLayer.mask = circleLayer

Now I would assume to get a half circle shape with a gradient. However, I get a circle with a gradient instead. It seem that when applying the circleLayer as mask to the gradientLayer the rect mask is not considered / applied...

Am I doing something wrong? Is there another way to solve this? Masking and combining masks is quite a common task in many different applications. Thus I wonder if such a powerful framework as CA has a way of doing this.

答案1

得分: 2

如@DuncanC指出,蒙版不能嵌套。因此,将Layer A应用为Layer B的蒙版,然后将Layer B应用为Layer C的蒙版并不起作用如预期。这只会使用B的原始内容作为蒙版,并不考虑Layer A所做的更改。

我通过首先“展平”Layer B + Mask Layer A的结果来绕过这个限制。通过创建此结果的图像,然后将其应用为Layer C的蒙版来实现:

let layerA = CAShapeLayer() // ... 添加一些形状
let layerB = CAShapeLayer() // ... 添加一些形状

// 将A应用为B的蒙版
layerB.mask = layerA

// 展平B
let flatB = layerB.flatten()

// 将flatB应用为C的蒙版
let layerC = CAShapeLayer() // ... 添加一些形状
layerC.mask = flatB

展平是使用以下CALayer扩展完成的:

extension CALayer {
    func flatten() -> CALayer {
        guard let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),
              let ctx = CGContext(data: nil, width: Int(bounds.width), height: Int(bounds.height), bitsPerComponent: 8, bytesPerRow: 4*Int(bounds.width), space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return CALayer() }

        ctx.translateBy(x: 0, y: bounds.height)
        ctx.scaleBy(x: 1.0, y: -1.0)

        render(in: ctx)
        let image = ctx.makeImage()

        let flattenedLayer = CALayer()
        flattenedLayer.frame = frame
        flattenedLayer.contents = image

        return flattenedLayer
    }
}

这可能不是完美的解决方案,但在我的情况下效果很好。也许这对其他人也有帮助。

英文:

As @DuncanC pointed out mask cannot be nested. So, applying Layer A as mask to Layer B and than applying Layer B as mask to Layer C does not work as expected. This will only use the original content of B as mask and not taking account the changed made by Layer A.

I was able to get around this limitation by first "flattening" the result of Layer B + Mask Layer A. This is done by creating an image of this result and than applying this as mask to Layer C:

let layerA = CAShapeLayer() // ... add some shapes
let layerB = CAShapeLayer() // ... add some shapes

// Apply A as mask to B
layerB.mask = layerA

// Flatten B
let flatB = layerB.flatten()

// Apply flatB as mask to C
let layerC = CAShapeLayer() // ... add some shapes
layerC.mask = flatB

The flattening is done using this CALayer extension:

extension CALayer {
    func flatten() -&gt; CALayer {
        guard let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),
              let ctx = CGContext(data: nil, width: Int(bounds.width), height: Int(bounds.height), bitsPerComponent: 8, bytesPerRow: 4*Int(bounds.width), space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return CALayer() }
    
        ctx.translateBy(x: 0, y: bounds.height)
        ctx.scaleBy(x: 1.0, y: -1.0)

        render(in: ctx)
        let image = ctx.makeImage()
    
        let flattenedLayer = CALayer()
        flattenedLayer.frame = frame
        flattenedLayer.contents = image
    
        return flattenedLayer
    }
}

This might not be the perfect solution but it works very well in my case. Maybe this might help others as well.

答案2

得分: 0

无法嵌套遮罩。您需要为要隐藏的每个图层创建一个唯一的遮罩。

英文:

Answer: You can't nest masks. You will need ot create a unique mask for each layer that masks the parts of that layer that you want to hide.

huangapple
  • 本文由 发表于 2023年3月7日 21:08:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75662388.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定