Swift |使用 HSL 颜色 space 而不是标准 HSB/HSV
Swift | Use HSL color space instead of standard HSB/HSV
我目前正在开发 HSL 颜色选择器,它需要 HSL 颜色 space。
但是据我发现,UIColor
和Color
都只能用brightness
初始化,而不是我需要的lightness
。
有没有办法使用 HSL 颜色 space 而不是默认的 HSB/HSV 颜色?
请问,从 HSB 转换为 HSL 的最佳做法是什么?
谢谢!
您可以简单地创建一个 HSL 颜色 space 初始值设定项。
注意:
//From HSL TO HSB ---------
部分的公式取自 https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_HSV
实现如下:
和UIColor
import UIKit
extension UIColor {
convenience init(hue: CGFloat, saturation: CGFloat, lightness: CGFloat, alpha: CGFloat) {
precondition(0...1 ~= hue &&
0...1 ~= saturation &&
0...1 ~= lightness &&
0...1 ~= alpha, "input range is out of range 0...1")
//From HSL TO HSB ---------
var newSaturation: CGFloat = 0.0
let brightness = lightness + saturation * min(lightness, 1-lightness)
if brightness == 0 { newSaturation = 0.0 }
else {
newSaturation = 2 * (1 - lightness / brightness)
}
//---------
self.init(hue: hue, saturation: newSaturation, brightness: brightness, alpha: alpha)
}
}
和Color
import SwiftUI
extension Color {
init(hue: Double, saturation: Double, lightness: Double, opacity: Double) {
precondition(0...1 ~= hue &&
0...1 ~= saturation &&
0...1 ~= lightness &&
0...1 ~= opacity, "input range is out of range 0...1")
//From HSL TO HSB ---------
var newSaturation: Double = 0.0
let brightness = lightness + saturation * min(lightness, 1-lightness)
if brightness == 0 { newSaturation = 0.0 }
else {
newSaturation = 2 * (1 - lightness / brightness)
}
//---------
self.init(hue: hue, saturation: newSaturation, brightness: brightness, opacity: opacity)
}
}
您还可以在创建时将 HSL 值转换为 RGB
public extension UIColor {
typealias RGBA = (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
var rgbaValues: RGBA {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
self.getRed(&r,
green: &g,
blue : &b,
alpha: &a
)
return (red:r, green:g, blue:b, alpha: a)
}
convenience init(hue h: CGFloat, saturation s: CGFloat, lightness l: CGFloat, alpha : CGFloat) {
let v = UIColor
.from(hue : h,
saturation: s,
lightness : l,
alpha : alpha)
.rgbaValues
self.init(red : v.red,
green : v.green,
blue : v.blue,
alpha : v.alpha
)
}
static func from(hue h: CGFloat, saturation s: CGFloat, lightness l: CGFloat, alpha a: CGFloat = 1.0) -> UIColor {
precondition(0...1 ~= h && 0...1 ~= s && 0...1 ~= l && 0...1 ~= a, "input range must be in 0...1" )
let h6: CGFloat = (h * 6.0).truncatingRemainder(dividingBy: 6.0)
let c: CGFloat = (1 - abs((2 * l) - 1)) * s
let x: CGFloat = c * (1 - abs(h6.truncatingRemainder(dividingBy: 2) - 1))
let m: CGFloat = l - (c / 2)
func rgbaFrom(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> RGBA { (red: r+m, green: g+m, blue: b+m, alpha: a) }
func color(from rgba: RGBA) -> UIColor { .init(red: rgba.red, green: rgba.green, blue: rgba.blue, alpha: rgba.alpha) }
switch h6 {
case 0..<1: return color(from: rgbaFrom(c,x,0))
case 1..<2: return color(from: rgbaFrom(x,c,0))
case 2..<3: return color(from: rgbaFrom(0,c,x))
case 3..<4: return color(from: rgbaFrom(0,x,c))
case 4..<5: return color(from: rgbaFrom(x,0,c))
case 5..<6: return color(from: rgbaFrom(c,0,x))
default: return .black
}
}
}
我目前正在开发 HSL 颜色选择器,它需要 HSL 颜色 space。
但是据我发现,UIColor
和Color
都只能用brightness
初始化,而不是我需要的lightness
。
有没有办法使用 HSL 颜色 space 而不是默认的 HSB/HSV 颜色?
请问,从 HSB 转换为 HSL 的最佳做法是什么?
谢谢!
您可以简单地创建一个 HSL 颜色 space 初始值设定项。
注意:
//From HSL TO HSB ---------
部分的公式取自 https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_HSV
实现如下:
和UIColor
import UIKit
extension UIColor {
convenience init(hue: CGFloat, saturation: CGFloat, lightness: CGFloat, alpha: CGFloat) {
precondition(0...1 ~= hue &&
0...1 ~= saturation &&
0...1 ~= lightness &&
0...1 ~= alpha, "input range is out of range 0...1")
//From HSL TO HSB ---------
var newSaturation: CGFloat = 0.0
let brightness = lightness + saturation * min(lightness, 1-lightness)
if brightness == 0 { newSaturation = 0.0 }
else {
newSaturation = 2 * (1 - lightness / brightness)
}
//---------
self.init(hue: hue, saturation: newSaturation, brightness: brightness, alpha: alpha)
}
}
和Color
import SwiftUI
extension Color {
init(hue: Double, saturation: Double, lightness: Double, opacity: Double) {
precondition(0...1 ~= hue &&
0...1 ~= saturation &&
0...1 ~= lightness &&
0...1 ~= opacity, "input range is out of range 0...1")
//From HSL TO HSB ---------
var newSaturation: Double = 0.0
let brightness = lightness + saturation * min(lightness, 1-lightness)
if brightness == 0 { newSaturation = 0.0 }
else {
newSaturation = 2 * (1 - lightness / brightness)
}
//---------
self.init(hue: hue, saturation: newSaturation, brightness: brightness, opacity: opacity)
}
}
您还可以在创建时将 HSL 值转换为 RGB
public extension UIColor {
typealias RGBA = (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
var rgbaValues: RGBA {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
self.getRed(&r,
green: &g,
blue : &b,
alpha: &a
)
return (red:r, green:g, blue:b, alpha: a)
}
convenience init(hue h: CGFloat, saturation s: CGFloat, lightness l: CGFloat, alpha : CGFloat) {
let v = UIColor
.from(hue : h,
saturation: s,
lightness : l,
alpha : alpha)
.rgbaValues
self.init(red : v.red,
green : v.green,
blue : v.blue,
alpha : v.alpha
)
}
static func from(hue h: CGFloat, saturation s: CGFloat, lightness l: CGFloat, alpha a: CGFloat = 1.0) -> UIColor {
precondition(0...1 ~= h && 0...1 ~= s && 0...1 ~= l && 0...1 ~= a, "input range must be in 0...1" )
let h6: CGFloat = (h * 6.0).truncatingRemainder(dividingBy: 6.0)
let c: CGFloat = (1 - abs((2 * l) - 1)) * s
let x: CGFloat = c * (1 - abs(h6.truncatingRemainder(dividingBy: 2) - 1))
let m: CGFloat = l - (c / 2)
func rgbaFrom(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> RGBA { (red: r+m, green: g+m, blue: b+m, alpha: a) }
func color(from rgba: RGBA) -> UIColor { .init(red: rgba.red, green: rgba.green, blue: rgba.blue, alpha: rgba.alpha) }
switch h6 {
case 0..<1: return color(from: rgbaFrom(c,x,0))
case 1..<2: return color(from: rgbaFrom(x,c,0))
case 2..<3: return color(from: rgbaFrom(0,c,x))
case 3..<4: return color(from: rgbaFrom(0,x,c))
case 4..<5: return color(from: rgbaFrom(x,0,c))
case 5..<6: return color(from: rgbaFrom(c,0,x))
default: return .black
}
}
}