lucasb-eyer/go-colorful

GitHub: lucasb-eyer/go-colorful

一个用于在 Go 语言中进行多种色彩空间转换、感知均匀颜色混合、距离比较及调色板生成的颜色处理库。

Stars: 1252 | Forks: 69

# go-colorful [![Go Reference](https://pkg.go.dev/badge/github.com/lucasb-eyer/go-colorful.svg)](https://pkg.go.dev/github.com/lucasb-eyer/go-colorful) [![go reportcard](https://goreportcard.com/badge/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° 中的距离是相同的,因为它们是同一个空间,只是后者使用了柱面坐标) ![颜色距离比较](https://static.pigsec.cn/wp-content/uploads/repos/cas/2f/2f92fb2fdec8efd608429daa86c2b29b99868ca46a6cf6939150b16844fdbaff.png) 上方显示的两种颜色比下方显示的两种颜色看起来差异要大得多。然而,在 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`)的: ![在不同空间中混合颜色。](https://static.pigsec.cn/wp-content/uploads/repos/cas/87/87be6d4aa6b38d070da2620c16d90dde184e7983e596c40d08eb5a505e638de0.png) 你可以看到 HSV 非常糟糕:它混入了一些绿色,而绿色在原始颜色中根本不存在!RGB 要好得多,但它停留在高亮度的区域有点太久了。LUV 和 LAB 都达到了合适的亮度,但 LAB 的色彩更丰富一些。HCL 的工作原理与 HSV 类似(都是柱面插值),但它正确地完成了任务:没有出现绿色,并且亮度是线性变化的。 虽然这看起来都很不错,但你需要知道一件事:在任何 CIE 色彩空间中进行插值时,你可能会得到无效的 RGB 颜色!如果起始和结束颜色是用户输入的或随机的,这一点尤为重要。发生这种情况的一个例子是在 `#eeef61` 和 `#1e3140` 之间进行混合时: ![在 CIE 空间中混合时可能会出现无效的 RGB 颜色。](https://static.pigsec.cn/wp-content/uploads/repos/cas/36/36518ec6563dddf8e047950db8700c1aff04a484c28c3a4f29d2f5d220bf9ad2.png) 你可以通过调用 `IsValid` 方法来测试一种颜色是否是有效的 RGB 颜色。事实上,对于底部偏红的颜色,调用 IsValid 将返回 false。 “修复”这个问题的一种方法是调用 `Clamped`,它会返回一个靠近该无效颜色的有效颜色,从而获得一个接近且有效的颜色。这样做后,我们得到了以下令人满意的结果: ![通过将无效 RGB 颜色限制在有效范围内来修复它们。](doc/colorblend/clamped.png> 以下是生成上述三张图片的代码;可以在 `doc/colorblend/colorblend.go` 中找到 ``` package main import "fmt" import "github.com/lucasb-eyer/go-colorful" import "image" import "image/draw" import "image/png" import "os" func main() { 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 空间中生成的华丽渐变吧: ![HCL 空间中的“光谱” colorbrewer 渐变。](https://static.pigsec.cn/wp-content/uploads/repos/cas/78/78712235b45d888afa7a2f209d0fe6e778221c69da3225d531cfb60c69454cac.png) ### 获取随机颜色 有时需要生成随机颜色。你可以简单地通过生成具有随机值的颜色来自行完成此操作。通过将随机值限制在小于 [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° 空间。下图显示了前两行为温暖的颜色,后两行为快乐的颜色。在这些颜色中,第一行是常规版本,第二行是快速版本。 ![分别代表温暖、快速温暖、快乐和快速快乐的随机颜色。](https://static.pigsec.cn/wp-content/uploads/repos/cas/7a/7aab04c4a8a0b9f4467b8ff21e29338364a48bdb2f74a58cc8454db752ded165.png) 不要忘记初始化随机种子!你可以在 `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)`。由于所有这些方法都包含一定的随机性,因此你的结果可能会有所不同。 ![所有示例调色板](https://static.pigsec.cn/wp-content/uploads/repos/cas/01/0109fd4c5d97f897389e78d3bfb0c05c45f49b573aa21cbacdbde1303921fa2d.png) 同样,用于生成上述图像的代码可在 [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` 的行为: ![排序颜色](https://static.pigsec.cn/wp-content/uploads/repos/cas/68/68beb97ab03d90ef28196fd5a03596a7335a0855ea8bd04b219fca68c7eacc48.png) 第一行是输入数据:一个包含 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)中查看。这是展示近似质量的主要图表: ![近似质量](https://static.pigsec.cn/wp-content/uploads/repos/cas/31/31dad8bb2d30c4313957bc30279450d4b92abe338cd05575ff617306d66f8442.png) 如果在许多地方使用 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工具, 图像处理, 开发工具库, 日志审计, 颜色空间, 颜色转换