How can I parse RGB color in web color format (3 or 6 hex digits) to Color
from image/color
? Does go have any built-in parser for that?
I want to be able to parse both #XXXXXX
and #XXX
colors formats.
color
docs says nothing about it: https://golang.org/pkg/image/color/
but this task is very common, so I beleive that go has some functions for that (which I just didn't find).
将十六进制字符串解析为图像/颜色
- 写回答
- 好问题 0 提建议
- 追加酬金
- 关注问题
- 邀请回答
-
3条回答 默认 最新
- duandai2178 2019-01-15 14:18关注
1. Elegant solution
Here's another solution using
fmt.Sscanf()
. It certainly not the fastest solution, but it is elegant. It scans right into the fields of acolor.RGBA
struct:func ParseHexColor(s string) (c color.RGBA, err error) { c.A = 0xff switch len(s) { case 7: _, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B) case 4: _, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B) // Double the hex digits: c.R *= 17 c.G *= 17 c.B *= 17 default: err = fmt.Errorf("invalid length, must be 7 or 4") } return }
Testing it:
hexCols := []string{ "#112233", "#123", "#000233", "#023", "invalid", "#abcd", "#-12", } for _, hc := range hexCols { c, err := ParseHexColor(hc) fmt.Printf("%-7s = %3v, %v ", hc, c, err) }
Output (try it on the Go Playground):
#112233 = { 17 34 51 255}, <nil> #123 = { 17 34 51 255}, <nil> #000233 = { 0 2 51 255}, <nil> #023 = { 0 34 51 255}, <nil> invalid = { 0 0 0 255}, input does not match format #abcd = { 0 0 0 255}, invalid length, must be 7 or 4 #-12 = { 0 0 0 255}, expected integer
2. Fast solution
If performance does matter,
fmt.Sscanf()
is a really bad choice. It requires a format string which the implementation has to parse, and according to it parse the input, and use reflection to store the result to the pointed values.Since the task is basically just "parsing" a hexadecimal value, we can do better than this. We don't even have to call into a general hex parsing library (such as
encoding/hex
), we can do that on our own. We don't even have to treat the input as astring
, or even as a series ofrune
s, we may lower to the level of treating it as a series of bytes. Yes, Go storesstring
values as UTF-8 byte sequences in memory, but if the input is a valid color string, all its bytes must be in the range of0..127
which map to bytes 1-to-1. If that would not be the case, the input would already be invalid, which we will detect, but what color we return in that case should not matter (does not matter).Now let's see a fast implementation:
var errInvalidFormat = errors.New("invalid format") func ParseHexColorFast(s string) (c color.RGBA, err error) { c.A = 0xff if s[0] != '#' { return c, errInvalidFormat } hexToByte := func(b byte) byte { switch { case b >= '0' && b <= '9': return b - '0' case b >= 'a' && b <= 'f': return b - 'a' + 10 case b >= 'A' && b <= 'F': return b - 'A' + 10 } err = errInvalidFormat return 0 } switch len(s) { case 7: c.R = hexToByte(s[1])<<4 + hexToByte(s[2]) c.G = hexToByte(s[3])<<4 + hexToByte(s[4]) c.B = hexToByte(s[5])<<4 + hexToByte(s[6]) case 4: c.R = hexToByte(s[1]) * 17 c.G = hexToByte(s[2]) * 17 c.B = hexToByte(s[3]) * 17 default: err = errInvalidFormat } return }
Testing it with the same inputs as in the first example, the output is (try it on the Go Playground):
#112233 = { 17 34 51 255}, <nil> #123 = { 17 34 51 255}, <nil> #000233 = { 0 2 51 255}, <nil> #023 = { 0 34 51 255}, <nil> invalid = { 0 0 0 255}, invalid format #abcd = { 0 0 0 255}, invalid format #-12 = { 0 17 34 255}, invalid format
3. Benchmarks
Let's benchmark these 2 solutions. The benchmarking code will include calling them with long and short formats. Error case is excluded.
func BenchmarkParseHexColor(b *testing.B) { for i := 0; i < b.N; i++ { ParseHexColor("#112233") ParseHexColor("#123") } } func BenchmarkParseHexColorFast(b *testing.B) { for i := 0; i < b.N; i++ { ParseHexColorFast("#112233") ParseHexColorFast("#123") } }
And here are the benchmark results:
go test -bench . -benchmem BenchmarkParseHexColor-4 500000 2557 ns/op 144 B/op 9 allocs/op BenchmarkParseHexColorFast-4 100000000 10.3 ns/op 0 B/op 0 allocs/op
As we can see, the "fast" solution is roughly 250 times faster and uses no allocation (unlike the "elegant" solution).
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报
悬赏问题
- ¥15 名为“Product”的列已属于此 DataTable
- ¥15 安卓adb backup备份应用数据失败
- ¥15 eclipse运行项目时遇到的问题
- ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
- ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
- ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站
- ¥50 成都蓉城足球俱乐部小程序抢票
- ¥15 yolov7训练自己的数据集
- ¥15 esp8266与51单片机连接问题(标签-单片机|关键词-串口)(相关搜索:51单片机|单片机|测试代码)
- ¥15 电力市场出清matlab yalmip kkt 双层优化问题