lucasb-eyer/go-colorful
GitHub: lucasb-eyer/go-colorful
一个用于在 Go 语言中进行多种色彩空间转换、感知均匀颜色混合、距离比较及调色板生成的颜色处理库。
Stars: 1252 | Forks: 69
# go-colorful
[](https://pkg.go.dev/github.com/lucasb-eyer/go-colorful)
[](https://goreportcard.com/report/github.com/lucasb-eyer/go-colorful)
一个用于在 Go 中处理颜色的库。支持 Go 1.13 及以上版本。
# 为什么开发此库?
我喜欢游戏。我制作游戏。我热爱细节,也常常沉迷于细节。
在开发 [Memory Which Does Not Suck](https://github.com/lucasb-eyer/mwdns/) 时,遇到了一个这样的细节:当时我们希望服务器能为玩家随机分配颜色。有时两名玩家会得到非常相似的颜色,这让我很困扰。就在同一天晚上,[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/) 登上了 HackerNews 的首页,向我展示了如何正确地解决这个问题™。最后,当时在 Go 中没有任何可用的处理色彩空间的库。Colorful 就是为了填补这个空白而诞生的,并且实现了 Go 的 `color.Color` 接口。
# 它是什么?
Go-Colorful 使用 RGB 存储颜色,并提供了将这些颜色转换到各种色彩空间的方法。目前支持的色彩空间有:
- **RGB:** 红、绿、蓝三个分量均在 [0..1] 之间。
- **HSL:** 色相 (Hue) 在 [0..360] 之间,饱和度 (Saturation) 和亮度 (Luminance) 在 [0..1] 之间。由于历史原因保留;请当它不存在。
- **HSV:** 色相 (Hue) 在 [0..360] 之间,饱和度 (Saturation) 和明度 (Value) 在 [0..1] 之间。建议使用 HCL,见下文。
- **Hex RGB:** “互联网”颜色格式,例如 #FF00FF。
- **Linear RGB:** 参见 [gamma 校正渲染](http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/)。
- **CIE-XYZ:** CIE 的标准色彩空间,取值几乎在 [0..1] 之间。
- **CIE-xyY:** 使用 x 和 y 编码色度,使用 Y 编码亮度,均在 [0..1] 之间。
- **CIE-L\*a\*b\*:** 一种*感知均匀*的色彩空间,即距离是有实际意义的。L\* 在 [0..1] 之间,a\* 和 b\* 几乎在 [-1..1] 之间。
- **CIE-L\*u\*v\*:** 与 CIE-L\*a\*b\* 非常相似,目前对于哪个“更好”[没有定论](http://en.wikipedia.org/wiki/CIELUV#Historical_background)。
- **CIE-L\*C\*h° (HCL):** 这通常是[最实用](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/)的一个;它是极坐标下的 CIE-L\*a\*b\* 空间,即一个*更好*的 HSV。H° 在 [0..360] 之间,C\* 几乎在 [0..1] 之间,L\* 与 CIE-L\*a\*b\* 中相同。
- **CIE LCh(uv):** 在代码中称为 `LuvLCh`,这是 CIE-L\*u\*v\* 色彩空间的柱状转换。类似于上面的 HCL:H° 在 [0..360] 之间,C\* 几乎在 [0..1] 之间,L\* 与 CIE-L\*u\*v\* 中相同。
- **HSLuv:** HSL 的更好替代品,参见[这里](https://www.hsluv.org/)和[这里](https://www.kuon.ch/post/2020-03-08-hsluv/)。色相 (Hue) 在 [0..360] 之间,饱和度 (Saturation) 和亮度 (Luminance) 在 [0..1] 之间。
- **HPLuv:** HSLuv 的一个变体。该色彩空间更平滑,但只能包含柔和色彩。由于有效颜色受到限制,很容易出现远高于 1.0 的无效饱和度值,这表明该颜色不是柔和色彩,无法在 HPLuv 中表示。
- **Oklab:** 由 Björn Ottosson 提出的一种感知色彩空间,它改进了 CIE-L\*a\*b\*,具有更好的感知均匀性,特别是在蓝色调方面。L 在 [0..1] 之间,a 和 b 大致在 [-0.5..0.5] 之间。参见 [Oklab](https://bottosson.github.io/posts/oklab/)。
- **Oklch:** Oklab 的柱状(极坐标)表示,类似于 HCL。L 在 [0..1] 之间,C 大致在 [0..0.5] 之间,h° 在 [0..360] 之间。
对于适用的色彩空间(XYZ, Lab, Luv, HCl),默认使用 [D65](http://en.wikipedia.org/wiki/Illuminant_D65) 作为参考白点,但也提供了使用自定义参考白点的方法。
某个坐标*几乎在*某个范围内,意味着通常情况下它确实如此,但对于非常明亮的颜色,或者取决于所使用的参考白点,它可能会略微超出这个范围。例如,#0000ff 的 C\* 值为 1.338。
已提供单元测试。
## 不错,但它有什么用?
- 转换色彩空间。有些人就喜欢这么做。
- 通过使用合适的色彩空间,以“自然”的外观混合(插值)颜色。
- 在某些约束条件下(例如相同色度的颜色,或同一种颜色的不同色调)生成随机颜色。
- 生成由相同色温且截然不同的颜色组成的华丽随机调色板。
# 那么我应该使用哪种色彩空间?
这取决于你想要做什么。我认为 *I want hue* 的作者们说得很对:RGB 适合*屏幕产生*颜色的方式,CIE L\*a\*b\* 适合*人类感知*颜色的方式,而 HCL 适合*人类思考*颜色的方式。
当你想要使用 HSV 时,不妨改用 CIE-L\*C\*h°。对于固定的亮度 L\* 和彩度 C\* 值,色相角 h° 会在具有相同感知亮度和强度的颜色之间旋转。
# 怎么用?
### 安装
安装本库非常简单:
```
$ go get github.com/lucasb-eyer/go-colorful
```
然后可以通过以下方式引入包:
```
import "github.com/lucasb-eyer/go-colorful"
```
### 基本用法
使用不同的源色彩空间创建一种美丽的蓝色:
```
// Any of the following should be the same
c := colorful.Color{0.313725, 0.478431, 0.721569}
c, err := colorful.Hex("#517AB8")
if err != nil {
log.Fatal(err)
}
c = colorful.Hsv(216.0, 0.56, 0.722)
c = colorful.Xyz(0.189165, 0.190837, 0.480248)
c = colorful.Xyy(0.219895, 0.221839, 0.190837)
c = colorful.Lab(0.507850, 0.040585,-0.370945)
c = colorful.Luv(0.507849,-0.194172,-0.567924)
c = colorful.Hcl(276.2440, 0.373160, 0.507849)
c = colorful.OkLab(0.577227, -0.021391, -0.104541)
c = colorful.OkLch(0.577227, 0.106707, 258.435657)
fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B)
```
然后将此颜色转换回各种色彩空间:
```
hex := c.Hex()
h, s, v := c.Hsv()
x, y, z := c.Xyz()
x, y, Y := c.Xyy()
l, a, b := c.Lab()
l, u, v := c.Luv()
h, c, l := c.Hcl()
l, a, b = c.OkLab()
l, c, h = c.OkLch()
```
请注意,由于 Go 语言要求首字母大写的无奈设定,与 xyY 空间相关的函数名称显得有些别扭。如果你有什么好建议,请提交一个 issue。(我不认为 `XyY` 是个好名字。)
### `color.Color` 接口
由于 `colorful.Color` 实现了 Go 的 `color.Color` 接口(位于 `image/color` 包中),它可以在任何期望接收 `color.Color` 的地方使用。
此外,你可以使用 `MakeColor` 函数将任何实现了 `color.Color` 接口的对象转换为 `colorful.Color`:
```
c, ok := colorful.MakeColor(color.Gray16{12345})
```
**注意事项:** 请注意,后一种转换(使用 `MakeColor`)在 alpha 值正好为零时会遇到边界情况。由于 `color.Color` 使用预乘 alpha 颜色,这意味着 RGB 值会丢失(被置为 0)并且无法恢复。在这种情况下,`MakeColor` 会返回 `false` 作为其第二个返回值。
### 比较颜色
在 RGB 色彩空间中,颜色之间的欧几里得距离*并不*对应于视觉/感知距离。这意味着在 RGB 空间中距离相同的两对颜色,在视觉上看起来可能相差甚远。而 CIE-L\*a\*b\*、CIE-L\*u\*v\* 和 CIE-L\*C\*h° 色彩空间则解决了这个问题。
因此,你应该只在这些空间中的任意一个中进行颜色比较。
(请注意,CIE-L\*a\*b\* 和 CIE-L\*C\*h° 中的距离是相同的,因为它们是同一个空间,只是后者使用了柱面坐标)

上方显示的两种颜色比下方显示的两种颜色看起来差异要大得多。然而,在 RGB 空间中,它们的距离是相同的。
这里有一个简单的示例程序,展示了上方和下方的两组颜色在 RGB、CIE-L\*a\*b\* 和 CIE-L\*u\*v\* 空间中的距离。你可以在 `doc/colordist/colordist.go` 中找到它。
```
package main
import "fmt"
import "github.com/lucasb-eyer/go-colorful"
func main() {
c1a := colorful.Color{150.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
c1b := colorful.Color{53.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
c2a := colorful.Color{10.0 / 255.0, 150.0 / 255.0, 50.0 / 255.0}
c2b := colorful.Color{99.9 / 255.0, 150.0 / 255.0, 10.0 / 255.0}
fmt.Printf("DistanceRgb: c1: %v\tand c2: %v\n", c1a.DistanceRgb(c1b), c2a.DistanceRgb(c2b))
fmt.Printf("DistanceLab: c1: %v\tand c2: %v\n", c1a.DistanceLab(c1b), c2a.DistanceLab(c2b))
fmt.Printf("DistanceLuv: c1: %v\tand c2: %v\n", c1a.DistanceLuv(c1b), c2a.DistanceLuv(c2b))
fmt.Printf("DistanceCIE76: c1: %v\tand c2: %v\n", c1a.DistanceCIE76(c1b), c2a.DistanceCIE76(c2b))
fmt.Printf("DistanceCIE94: c1: %v\tand c2: %v\n", c1a.DistanceCIE94(c1b), c2a.DistanceCIE94(c2b))
fmt.Printf("DistanceCIEDE2000: c1: %v\tand c2: %v\n", c1a.DistanceCIEDE2000(c1b), c2a.DistanceCIEDE2000(c2b))
}
```
运行上述程序表明,你始终应该优先选择 CIE 系列的距离计算方法:
```
$ go run colordist.go
DistanceRgb: c1: 0.3803921568627451 and c2: 0.3858713931171159
DistanceLab: c1: 0.32048458312798056 and c2: 0.24397151758565272
DistanceLuv: c1: 0.5134369614199698 and c2: 0.2568692839860636
DistanceCIE76: c1: 0.32048458312798056 and c2: 0.24397151758565272
DistanceCIE94: c1: 0.19799168128511324 and c2: 0.12207136371167401
DistanceCIEDE2000: c1: 0.17274551120971166 and c2: 0.10665210031428465
```
它还表明,`DistanceLab` 也就是正式场合所称的 `DistanceCIE76`,并且已经被稍微更准确、但计算开销也更大的 `DistanceCIE94` 和 `DistanceCIEDE2000` 所取代。
请注意,提供 `AlmostEqualRgb` 主要是为了(单元)测试目的。只有在你确切知道自己在做什么的情况下才使用它。否则它会坑惨你。
### 混合颜色
混合与距离高度相关,因为它基本上是在色彩空间中“穿行”,因此,如果色彩空间能很好地映射距离,这种穿行就是“平滑”的。
Colorful 提供了在 RGB、HSV、Oklab、Oklch 以及任何 CIE-LAB 空间中的混合函数。
当然,你肯定更希望使用 LAB 空间的混合函数,因为这些空间能更好地映射距离,但以防万一,这里有一个示例向你展示了在不同空间中是如何进行混合(从 `#fdffcc` 到 `#242a42`)的:

你可以看到 HSV 非常糟糕:它混入了一些绿色,而绿色在原始颜色中根本不存在!RGB 要好得多,但它停留在高亮度的区域有点太久了。LUV 和 LAB 都达到了合适的亮度,但 LAB 的色彩更丰富一些。HCL 的工作原理与 HSV 类似(都是柱面插值),但它正确地完成了任务:没有出现绿色,并且亮度是线性变化的。
虽然这看起来都很不错,但你需要知道一件事:在任何 CIE 色彩空间中进行插值时,你可能会得到无效的 RGB 颜色!如果起始和结束颜色是用户输入的或随机的,这一点尤为重要。发生这种情况的一个例子是在 `#eeef61` 和 `#1e3140` 之间进行混合时:

你可以通过调用 `IsValid` 方法来测试一种颜色是否是有效的 RGB 颜色。事实上,对于底部偏红的颜色,调用 IsValid 将返回 false。
“修复”这个问题的一种方法是调用 `Clamped`,它会返回一个靠近该无效颜色的有效颜色,从而获得一个接近且有效的颜色。这样做后,我们得到了以下令人满意的结果:
 {
blocks := 10
blockw := 40
img := image.NewRGBA(image.Rect(0,0,blocks*blockw,200))
c1, _ := colorful.Hex("#fdffcc")
c2, _ := colorful.Hex("#242a42")
// Use these colors to get invalid RGB in the gradient.
//c1, _ := colorful.Hex("#EEEF61")
//c2, _ := colorful.Hex("#1E3140")
for i := 0 ; i < blocks ; i++ {
draw.Draw(img, image.Rect(i*blockw, 0,(i+1)*blockw, 40), &image.Uniform{c1.BlendHsv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
draw.Draw(img, image.Rect(i*blockw, 40,(i+1)*blockw, 80), &image.Uniform{c1.BlendLuv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
draw.Draw(img, image.Rect(i*blockw, 80,(i+1)*blockw,120), &image.Uniform{c1.BlendRgb(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
draw.Draw(img, image.Rect(i*blockw,120,(i+1)*blockw,160), &image.Uniform{c1.BlendLab(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
// This can be used to "fix" invalid colors in the gradient.
//draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1)).Clamped()}, image.Point{}, draw.Src)
}
toimg, err := os.Create("colorblend.png")
if err != nil {
fmt.Printf("Error: %v", err)
return
}
defer toimg.Close()
png.Encode(toimg, img)
}
```
#### 生成颜色渐变
混合颜色的一个常见原因是创建渐变。在 [doc/gradientgen.go](doc/gradientgen/gradientgen.go) 中有一个示例程序;它没有使用任何在之前的示例代码中未曾用过的 API,所以我就不在这里粘贴代码了。看看它在 HCL 空间中生成的华丽渐变吧:

### 获取随机颜色
有时需要生成随机颜色。你可以简单地通过生成具有随机值的颜色来自行完成此操作。通过将随机值限制在小于 [0..1] 的范围内,并使用诸如 CIE-H\*C\*l° 或 HSV 之类的空间,你可以生成同一种颜色的随机色调,或者具有特定亮度的随机颜色:
```
random_blue := colorful.Hcl(180.0+rand.Float64()*50.0, 0.2+rand.Float64()*0.8, 0.3+rand.Float64()*0.7)
random_dark := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), rand.Float64()*0.4)
random_light := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), 0.6+rand.Float64()*0.4)
```
由于获取随机“温暖”和“快乐”的颜色是一项相当常见的任务,因此提供了一些辅助函数:
```
colorful.WarmColor()
colorful.HappyColor()
colorful.FastWarmColor()
colorful.FastHappyColor()
```
以 `Fast` 为前缀的函数速度更快,但连贯性较差,因为它们使用的是 HSV 空间;而常规函数使用的是 CIE-L\*C\*h° 空间。下图显示了前两行为温暖的颜色,后两行为快乐的颜色。在这些颜色中,第一行是常规版本,第二行是快速版本。

不要忘记初始化随机种子!你可以在 `doc/colorgens/colorgens.go` 中看到用于生成此图片的代码。
### 获取随机调色板
一旦你需要生成不止一种随机颜色,你可能就会希望它们是可区分的。与一个拥有几乎和我一样蓝色颜色的对手对战并不好玩。这就是随机调色板可以派上用场的地方。
这些调色板是使用一种算法生成的,该算法确保调色板上的所有颜色尽可能地易于区分。同样,有一个 `Fast` 方法在 HSV 空间中工作,感知均匀性较差;非 `Fast` 方法在 CIE 空间中工作。关于 `SoftPalette` 的更多理论,请查看 [I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php)。再一次强调,这里有 `Happy` 和 `Warm` 版本,它们会做你期望的事情,但现在多了一个 `Soft` 版本,它的可配置性更强:你可以对色彩空间施加约束,以便获得具有特定*感觉*的颜色。
让我们先从简单的方法开始,它们唯一需要的参数就是要生成的颜色数量,例如,可以是玩家数量。它们会返回一个 `colorful.Color` 对象的数组:
```
pal1, err1 := colorful.WarmPalette(10)
pal2 := colorful.FastWarmPalette(10)
pal3, err3 := colorful.HappyPalette(10)
pal4 := colorful.FastHappyPalette(10)
pal5, err5 := colorful.SoftPalette(10)
```
请注意,如果你要求的颜色数量太多,非快速方法*可能*会失败。
让我们继续看高级方法,即 `SoftPaletteEx`。除了颜色数量之外,该函数还接受一个 `SoftPaletteSettings` 对象作为参数。这里有趣的部分是它的 `CheckColor` 成员,这是一个接受三个浮点数作为参数的布尔函数:`l`、`a` 和 `b`。对于位于你想要区域内的颜色,该函数应返回 `true`,否则返回 `false`。其他成员包括 `Iteration`,其值应在 [5..100] 之间,值越大意味着速度越慢但调色板越精确;还有 `ManySamples`,如果你的 `CheckColor` 约束拒绝了大部分色彩空间,你应该将其设置为 `true`。
例如,要创建一个包含 10 种棕色调颜色的调色板,你可以这样调用它:
```
func isbrowny(l, a, b float64) bool {
h, c, L := colorful.LabToHcl(l, a, b)
return 10.0 < h && h < 50.0 && 0.1 < c && c < 0.5 && L < 0.5
}
// Since the above function is pretty restrictive, we set ManySamples to true.
brownies := colorful.SoftPaletteEx(10, colorful.SoftPaletteSettings{isbrowny, 50, true})
```
下图显示了所有这些方法生成的调色板(源代码在 `doc/palettegens/palettegens.go` 中),按它们被介绍的顺序排列,即从上到下依次为:`Warm`、`FastWarm`、`Happy`、`FastHappy`、`Soft`、`SoftEx(isbrowny)`。由于所有这些方法都包含一定的随机性,因此你的结果可能会有所不同。

同样,用于生成上述图像的代码可在 [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go) 中找到。
### 排序颜色
排序颜色并不是一个定义明确的操作。例如,如果深色应该排在浅色之前,那么 {深蓝色,深红色,浅蓝色,浅红色} 已经是有序的;但如果长波长的颜色应该排在短波长的颜色之前,就需要重新排序为 {深红色,浅红色,深蓝色,浅蓝色}。
Go-Colorful 的 `Sorted` 函数会对一个颜色列表进行排序,以最小化相邻颜色(包括首尾颜色)之间的平均距离。(`Sorted` 不一定能找到绝对的最小值,只会找到一个相当接近的近似值。)下图由 [doc/colorsort/colorsort.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/colorsort/colorsort.go) 绘制,说明了 `Sorted` 的行为:

第一行是输入数据:一个包含 512 种随机选择颜色的切片。第二行显示了在 CIE-L\*C\*h° 空间中排序的颜色,首先按亮度 (L) 排序,然后按色相角 (h) 排序,最后按彩度 (C) 排序。注意,颜色中充满了令人分心的条纹。事实上,使用*任何*色彩空间和*任何*通道顺序进行排序都会产生类似的条纹图案。图像的第三行是使用 Go-Colorful 的 `Sorted` 函数进行排序的结果。虽然这些颜色看起来似乎没有任何特定的顺序,但这个序列至少看起来比按通道排序的序列更平滑。
### 使用 Linear RGB 进行计算
有两种方法可以转换 RGB 和 Linear RGB:一种是速度快且几乎精确的方法,另一种是速度慢但极其精确的方法。
```
r, g, b := colorful.Hex("#FF0000").FastLinearRgb()
```
TODO: 描述更多内容。
### 想要使用其他参考点?
```
c := colorful.LabWhiteRef(0.507850, 0.040585,-0.370945, colorful.D50)
l, a, b := c.LabWhiteRef(colorful.D50)
```
### 从数据库读取和写入颜色
`HexColor` 类型使得将颜色作为字符串存储在数据库中变得非常容易。它实现了 [https://godoc.org/database/sql#Scanner](database/sql.Scanner) 和 [database/sql/driver.Value](https://godoc.org/database/sql/driver.Value) 接口,提供了自动类型转换功能。
示例:
```
var hc HexColor
_, err := db.QueryRow("SELECT '#ff0000';").Scan(&hc)
// hc == HexColor{R: 1, G: 0, B: 0}; err == nil
```
# 常见问题解答
### 问:我得到了一堆乱七八糟的值!你的库太烂了!
答:你提供的值范围可能不对。例如,RGB 值应该在 0 和 1 之间,*而不是* 0 和 255 之间。请对你的颜色进行归一化处理。
### 问:Lab/Luv/HCl 似乎出问题了!你的库太烂了!
它们看起来像这样:
答:你很可能正在尝试生成并显示 RGB(因此也包括显示器)无法表示的颜色。例如,当你尝试转换类似 `HCL(190.0, 1.0, 1.0).RGB255()` 的颜色时,你要求的 RGB 值为 `(-2105.254 300.680 286.185)`,这显然是不存在的。而 `RGB255` 函数只是将这些数字直接转换为 `uint8`,从而产生了数据溢出和看起来完全损坏的渐变。你想要做的是:要么使用在 RGB 中实际存在的更合理的颜色值,要么对生成的颜色调用 `Clamp()` 以获取最接近的有效颜色,并接受这种妥协带来的后果:
`HCL(190.0, 1.0, 1.0).Clamp().RGB255()`。它看起来会像这样:
[这里有一个深入探讨此问题的 issue](https://github.com/lucasb-eyer/go-colorful/issues/14),以及[我的回答](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385),其中包含代码和漂亮的图片。还要注意的是,这个问题在上面的[“混合颜色”部分](https://github.com/lucasb-eyer/go-colorful#blending-colors)中已经有所提及。
### 问:在紧凑的循环中,转换到 Lab/Luv/HCl/... 的操作太慢了!
答:是的,确实如此。
这个库的目标是正确性、可读性和模块化;编写时并没有把速度放在首位。很大一部分缓慢来自于这些转换需要经过使用了幂运算的 `LinearRgb`。我通过使用泰勒展开式实现了一个名为 `FastLinearRgb` 的 `LinearRgb` 快速近似算法。这种近似方法大约快了 5 倍,精度高达约 0.5%,最大的隐患在于如果输入值超出了 0-1 的范围,准确率会急剧下降。
你可以按如下方式在你的转换中使用它:
```
col := // Get your color somehow
l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb()))
```
如果你需要利用这种快速近似的更快速的 `Distance*` 和 `Blend*` 版本,请随时实现它们并提交 pull-request,我很乐意接受。
这些函数的推导过程可以在[这个 Jupyter notebook](doc/LinearRGB Approximations.ipynb)中查看。这是展示近似质量的主要图表:

如果在许多地方使用 SIMD 指令,还可以获得更高的速度。你也可以通过近似完整的转换函数来获得特定转换的更高速度,但这超出了本库的范围。
感谢 [@ZirconiumX](https://github.com/ZirconiumX) 开启了这项调查,详情请参阅 [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18)。
### 问:为什么 `MakeColor` 会失败!?
答:当 alpha 通道为零时,`MakeColor` 会失败。在这种情况下,转换是未定义的。请参阅 [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) 以及上方的["`color.Color` 接口"](README.md#the-colorcolor-interface)部分中的简短注意事项说明。
# 开发者是谁?
这个库最初由 Lucas Beyer 开发,Bastien Dejean (@baskerville)、Phil Kulak (@pkulak)、Christian Muehlhaeuser (@muesli)、Scott Pakin (@spakin) 等人也做出了重要贡献。完整名单请查看[贡献者列表](https://github.com/lucasb-eyer/go-colorful/graphs/contributors)。
目前由 makeworld (@makew0rld) 维护。
## 许可证
本仓库采用 MIT 许可证,详情请参阅 [LICENSE](LICENSE)。
答:你很可能正在尝试生成并显示 RGB(因此也包括显示器)无法表示的颜色。例如,当你尝试转换类似 `HCL(190.0, 1.0, 1.0).RGB255()` 的颜色时,你要求的 RGB 值为 `(-2105.254 300.680 286.185)`,这显然是不存在的。而 `RGB255` 函数只是将这些数字直接转换为 `uint8`,从而产生了数据溢出和看起来完全损坏的渐变。你想要做的是:要么使用在 RGB 中实际存在的更合理的颜色值,要么对生成的颜色调用 `Clamp()` 以获取最接近的有效颜色,并接受这种妥协带来的后果:
`HCL(190.0, 1.0, 1.0).Clamp().RGB255()`。它看起来会像这样:
[这里有一个深入探讨此问题的 issue](https://github.com/lucasb-eyer/go-colorful/issues/14),以及[我的回答](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385),其中包含代码和漂亮的图片。还要注意的是,这个问题在上面的[“混合颜色”部分](https://github.com/lucasb-eyer/go-colorful#blending-colors)中已经有所提及。
### 问:在紧凑的循环中,转换到 Lab/Luv/HCl/... 的操作太慢了!
答:是的,确实如此。
这个库的目标是正确性、可读性和模块化;编写时并没有把速度放在首位。很大一部分缓慢来自于这些转换需要经过使用了幂运算的 `LinearRgb`。我通过使用泰勒展开式实现了一个名为 `FastLinearRgb` 的 `LinearRgb` 快速近似算法。这种近似方法大约快了 5 倍,精度高达约 0.5%,最大的隐患在于如果输入值超出了 0-1 的范围,准确率会急剧下降。
你可以按如下方式在你的转换中使用它:
```
col := // Get your color somehow
l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb()))
```
如果你需要利用这种快速近似的更快速的 `Distance*` 和 `Blend*` 版本,请随时实现它们并提交 pull-request,我很乐意接受。
这些函数的推导过程可以在[这个 Jupyter notebook](doc/LinearRGB Approximations.ipynb)中查看。这是展示近似质量的主要图表:

如果在许多地方使用 SIMD 指令,还可以获得更高的速度。你也可以通过近似完整的转换函数来获得特定转换的更高速度,但这超出了本库的范围。
感谢 [@ZirconiumX](https://github.com/ZirconiumX) 开启了这项调查,详情请参阅 [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18)。
### 问:为什么 `MakeColor` 会失败!?
答:当 alpha 通道为零时,`MakeColor` 会失败。在这种情况下,转换是未定义的。请参阅 [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) 以及上方的["`color.Color` 接口"](README.md#the-colorcolor-interface)部分中的简短注意事项说明。
# 开发者是谁?
这个库最初由 Lucas Beyer 开发,Bastien Dejean (@baskerville)、Phil Kulak (@pkulak)、Christian Muehlhaeuser (@muesli)、Scott Pakin (@spakin) 等人也做出了重要贡献。完整名单请查看[贡献者列表](https://github.com/lucasb-eyer/go-colorful/graphs/contributors)。
目前由 makeworld (@makew0rld) 维护。
## 许可证
本仓库采用 MIT 许可证,详情请参阅 [LICENSE](LICENSE)。标签:EVTX分析, Go, Ruby工具, 图像处理, 开发工具库, 日志审计, 颜色空间, 颜色转换