【问题标题】:In Swift calculate color without transparency obtained by overlaying a color with an transparant color在 Swift 中,通过用透明颜色覆盖颜色来计算没有透明度的颜色
【发布时间】:2018-08-03 19:35:31
【问题描述】:

  • 我有一个原始 UIView,它的颜色不透明 orgColor
  • 这是由一个覆盖 UIView 覆盖的,该 UIView 具有一个具有透明度的 overlayColor(alpha 通道)。

鉴于orgColoroverlayColor 这两种颜色,我如何计算应该没有任何透明度但屏幕上的颜色完全相同的calculatedColor,然后就好像两个视图实际上都一样被覆盖?

我为此编写了一个骨架函数(以显示原理)。但不知道如何计算(我在堆栈溢出上发现了一些关于颜色混合的帖子,但没有一个会真正消除结果中的透明度......):

func calculateColor(orgColor: UIColor, overlayColor: UIColor) -> UIColor {
    var (r1, g1, b1, a1) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0))
    var (r2, g2, b2, a2) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0))

    orgColor.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
    overlayColor.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)

    //how to put the alpha in the r g b? and integrate the overlay r g b?

    return color
}

【问题讨论】:

    标签: swift colors


    【解决方案1】:

    覆盖颜色的 alpha (a2) 告诉您颜色占总颜色的百分比,(1 - a2) 告诉您原始颜色的百分比。由此,这是一个简单的计算:

    func calculateColor(orgColor: UIColor, overlayColor: UIColor) -> UIColor {
        // Helper function to clamp values to range (0.0 ... 1.0)
        func clampValue(_ v: CGFloat) -> CGFloat {
            guard v > 0 else { return  0 }
            guard v < 1 else { return  1 }
            return v
        }
    
        var (r1, g1, b1, a1) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0))
        var (r2, g2, b2, a2) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0))
    
        orgColor.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
        overlayColor.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
    
        // Make sure the input colors are well behaved
        // Components should be in the range (0.0 ... 1.0)
        r1 = clampValue(r1)
        g1 = clampValue(g1)
        b1 = clampValue(b1)
    
        r2 = clampValue(r2)
        g2 = clampValue(g2)
        b2 = clampValue(b2)
        a2 = clampValue(a2)
    
        let color = UIColor(  red: r1 * (1 - a2) + r2 * a2,
                            green: g1 * (1 - a2) + g2 * a2,
                             blue: b1 * (1 - a2) + b2 * a2,
                            alpha: 1)
    
        return color
    }
    

    直观地说,这检查出来了。如果覆盖颜色完全不透明 (a2 == 1),则结果将是覆盖颜色。如果覆盖颜色完全透明 (a2 == 0),则结果将是原始颜色。如果叠加颜色的 alpha 为 0.5,您将获得恰好介于两种颜色之间的混合色。


    在 Playground 中测试:

    // Make these colors anything you want
    let c1 = UIColor(red: 27/255, green: 155/255, blue: 199/255, alpha: 1.0)
    let c2 = UIColor(red: 188/255, green: 13/255, blue: 51/255, alpha: 0.37)
    
    let c3 = calculateColor(orgColor: c1, overlayColor: c2)
    
    let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    let view2 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    
    view1.backgroundColor = c1
    view2.backgroundColor = c2
    
    // Overlay view2 onto view1
    view1.addSubview(view2)
    
    // view3 has the calculated color
    let view3 = UIView(frame: CGRect(x: 100, y: 0, width: 100, height: 100))
    view3.backgroundColor = c3
    
    // display the views side by side in view4
    let view4 = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
    view4.addSubview(view1)
    view4.addSubview(view3)
    
    // display this view and see that the two halves match
    view4
    

    游乐场输出:

    【讨论】:

    • vacawama 这完美!非常好的格式和完整的答案。只是一个评论:在颜色计算中,我们是否应该使用 min (..., 1.0) 函数输入不能大于 1.0 的值(在其他混合计算的其他答案中找到)?
    • 检查我写的扩展(感谢你!),我输入了 min 函数
    • min没有必要如果输入值在0.0 ... 1.0范围内,因为它们应该是。即使 r11.0 并且 r21.0 (它们的最大值),r1 * (1 - a2) + r2 * a2 的组合将在 1.0 处达到最大值。也就是说,可以使用超出范围的值创建UIColors,即使它们被解释为限制在该范围内,UIColor.getRed(green:blue:alpha:) 仍将返回超出范围的值。要使用这些 bad 颜色获得正确的结果,有必要限制范围。请参阅上面的修复程序。
    【解决方案2】:

    基于@vacawama 的回答(全归功于他!)我为 UIColor 编写了以下扩展:

    extension UIColor {
    
        func solidColorWhenOverlayed(by overlayColor: UIColor) -> UIColor {
            // Helper function to clamp values to range (0.0 ... 1.0)
            func clampValue(_ v: CGFloat) -> CGFloat {
                guard v > 0 else { return  0 }
                guard v < 1 else { return  1 }
                return v
            }
    
            var (r1, g1, b1, a1) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0))
            var (r2, g2, b2, a2) = (CGFloat(0), CGFloat(0), CGFloat(0), CGFloat(0))
    
            getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
            overlayColor.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
    
            // Make sure the input colors are well behaved
            // Components should be in the range (0.0 ... 1.0)
            // This to compensate for any "bad" colors = colors which have component values out of range
            r1 = clampValue(r1)
            g1 = clampValue(g1)
            b1 = clampValue(b1)
    
            r2 = clampValue(r2)
            g2 = clampValue(g2)
            b2 = clampValue(b2)
            a2 = clampValue(a2)
    
            return UIColor(  red  : r1 * (1 - a2) + r2 * a2,
                             green: g1 * (1 - a2) + g2 * a2,
                             blue : b1 * (1 - a2) + b2 * a2,
                             alpha: 1)
        }
    
    }
    

    【讨论】:

    • 风格问题:我建议self.getRed 而不是getRed,因为它更清楚发生了什么。请参阅我在上面提出的钳位问题。您的 min 解决方案将无法帮助 bad 输入颜色。
    • 我喜欢这个扩展。您可能想将其命名为 solidColorWhenOverlayed(by overlayColor: UIColor),这更符合 Swift 3/4 的冗余风格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 2012-07-11
    • 2011-10-06
    • 2016-04-24
    相关资源
    最近更新 更多