👔view.inverseMask()

自製的遮罩,作用剛好跟官方的 .mask() 相反。

細節說明

在 .inverseMask() 中用到的 .foregroundColor(.black) 和 .backgroud(Color.white),對 Image 來說沒有任何作用,如下圖所示:

但 .luminanceToAlpha() 會將原圖中「黑色的部分」轉為「透明」(opacity = 0),而「白色的部分」則轉為「全黑」(opacity = 1),這時的圖有點像相片的「底片」(負片)。

套用 .mask() 的時候,opacity = 1 的會「保留原來的色彩」,opacity = 0 的部分則轉為「透明」。這就是爲什麼原圖小狗的眼睛四周比較黑,會造成最後的圖在小狗眼睛四周有些透明。

總之,mask 原圖中比較白的部分,作為「被剪裁對象」的相對部分會被保留下來,mask 原圖中較黑的部分則轉為透明,至於超出 mask 原圖範圍的部分,則直接被裁掉。 ⚠️ 但注意:做完 .mask() 之後,frame 的大小還是原來「被剪裁對象」的大小喔!

對於一張照片 photo 來說: paper.inverseMask(photo) == paper.mask(photo.luminanceToAlpha()) photo 較淡的部分,paper 會變透明一些。

但 paper.mask(photo) 則會完全保留 paper 的顏色,只把 paper 在 photo 以外的部分裁掉而已,這可能是因為 photo 照片本來的每個像素 opacity = 1 的緣故。

import SwiftUI
import PlaygroundSupport

extension View {
    @ViewBuilder
    func label(_ text: String) -> some View {
        VStack(alignment: .leading) {
            self.border(Color.black)
            Text(text).padding(.leading).padding(.bottom, 10)
        }.border(Color.black)
    }
}

let shad = Color.black.opacity(0.8)
let numberPaper = Image("numbers")).resizable().scaledToFill()
    .rotationEffect(.degrees(90)).frame(maxWidth: 280)

struct ContentView: View {
    
    let puppy: some View = Image("puppy")).resizable().scaledToFit().frame(width: 150)
    
    var v1: some View { puppy.foregroundColor(.black) } // opacity = 0 (hide)
    var v2: some View { v1.background(Color.white) }   // opacity = 1 (show)
    var v3: some View { v2.compositingGroup() }        // ⭐️ composite all layers
    var v4: some View { v3.luminanceToAlpha() }        // ⭐️ turn luminance into alpha
    
    var v5: some View {
        ZStack {
            numberPaper.mask(v4).shadow(color: shad, radius: 4, x: 4, y: 4) 
            puppy.hidden().border(Color.black)
        }.background(Gradient.down(.yellow, .green))
    }
    
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                puppy.label("image")
                v1   .label("fg = black")
                v2   .label("bg = white")
            }
            
            HStack(alignment: .top) {
                v4.background(Color.white).label("亮度轉 alpha")
                v5.label("mask(image)")
            }
            
        } // container (HStack)
            .padding()
            .background(Color.gray)
        
    } // body
}

PlaygroundPage.current.setLiveView(ContentView())

Last updated

Was this helpful?