为了账号安全,请及时绑定邮箱和手机立即绑定

将十六进制字符串解析为图像/颜色

将十六进制字符串解析为图像/颜色

Go
呼唤远方 2023-06-01 15:15:30
如何将Web 颜色格式(3 或 6 个十六进制数字)中的 RGB 颜色解析为Colorfrom image/color?go 有内置的解析器吗?我希望能够解析两种格式#XXXXXX和#XXX颜色格式。 color文档对此只字不提: https: //golang.org/pkg/image/color/ 但这个任务很常见,所以我相信 go 有一些功能(我只是没有找到)。
查看完整描述

3 回答

?
米琪卡哇伊

TA贡献1998条经验 获得超6个赞

前言:我在 中发布了此实用程序(2.Fast 解决方案)github.com/icza/gox,请参阅colorx.ParseHexColor()


1.优雅的解决方案

这是另一个使用fmt.Sscanf(). 它当然不是最快的解决方案,但它很优雅。它直接扫描到结构的字段中color.RGBA

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

}

测试它:


hexCols := []string{

    "#112233",

    "#123",

    "#000233",

    "#023",

    "invalid",

    "#abcd",

    "#-12",

}

for _, hc := range hexCols {

    c, err := ParseHexColor(hc)

    fmt.Printf("%-7s = %3v, %v\n", hc, c, err)

}

输出(在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.快速解决

如果性能确实很重要,fmt.Sscanf()那是一个非常糟糕的选择。它需要一个实现必须解析的格式字符串,并根据它解析输入,并使用反射将结果存储到指向的值中。

由于任务基本上只是“解析”一个十六进制值,我们可以做得更好。我们甚至不必调用通用的十六进制解析库(例如encoding/hex),我们可以自己完成。我们甚至不必将输入视为 a string,甚至不必将其视为一系列runes,我们可能会降低到将其视为一系列字节的级别。是的,Go 在内存中将值存储为 UTF-8 字节序列,但如果输入是有效的颜色字符串,则其所有字节必须在1 对 1 映射到字节string的范围内。0..127如果不是这种情况,输入将已经无效,我们将检测到这一点,但在这种情况下我们返回什么颜色应该无关紧要(无关紧要)。

现在让我们看一个快速的实现:

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

}

使用与第一个示例相同的输入对其进行测试,输出为(在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. 基准

让我们对这两个解决方案进行基准测试。基准测试代码将包括使用长格式和短格式调用它们。排除错误情况。


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")

    }

}

以下是基准测试结果:


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

正如我们所见,“快速”解决方案大约快250 倍并且不使用分配(与“优雅”解决方案不同)。


查看完整回答
反对 回复 2023-06-01
?
炎炎设计

TA贡献1808条经验 获得超4个赞

RGBA 颜色只有 4 个字节,红色、绿色、蓝色和 alpha 通道各一个。对于三个或六个十六进制数字,字母字节通常隐含为 0xFF(AABBCC被认为与 一样AABBCCFF,按原样ABC)。


因此解析颜色字符串就像对其进行规范化一样简单,使其具有“RRGGBBAA”(4 个十六进制编码字节)的形式,然后对其进行解码:


package main


import (

    "encoding/hex"

    "fmt"

    "image/color"

    "log"

)


func main() {

    colorStr := "102030FF"


    colorStr, err := normalize(colorStr)

    if err != nil {

        log.Fatal(err)

    }


    b, err := hex.DecodeString(colorStr)

    if err != nil {

        log.Fatal(err)

    }


    color := color.RGBA{b[0], b[1], b[2], b[3]}


    fmt.Println(color) // Output: {16 32 48 255}

}


func normalize(colorStr string) (string, error) {

    // left as an exercise for the reader

    return colorStr, nil

}

在操场上试试:https ://play.golang.org/p/aCX-vyfMG4G


查看完整回答
反对 回复 2023-06-01
?
MMMHUHU

TA贡献1834条经验 获得超8个赞

您可以使用将任意 2 个十六进制数字转换为整数strconv.ParseUint

strconv.ParseUint(str, 16, 8)

指示16基数 16(十六进制),8 指示位数,在本例中为一个字节。

您可以使用它来将每 2 个字符解析为它们的组件

https://play.golang.org/p/B56B8_NvnVR

func ParseHexColor(v string) (out color.RGBA, err error) {

    if len(v) != 7 {

        return out, errors.New("hex color must be 7 characters")

    }

    if v[0] != '#' {

        return out, errors.New("hex color must start with '#'")

    }

    var red, redError = strconv.ParseUint(v[1:3], 16, 8)

    if redError != nil {

        return out, errors.New("red component invalid")

    }

    out.R = uint8(red)

    var green, greenError = strconv.ParseUint(v[3:5], 16, 8)

    if greenError != nil {

        return out, errors.New("green component invalid")

    }

    out.G = uint8(green)

    var blue, blueError = strconv.ParseUint(v[5:7], 16, 8)

    if blueError != nil {

        return out, errors.New("blue component invalid")

    }

    out.B = uint8(blue)

    return

}


查看完整回答
反对 回复 2023-06-01
  • 3 回答
  • 0 关注
  • 201 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号