Skip to content

fix(convert): zero-pad each channel in RgbToHex - #120

Merged
inhere merged 1 commit into
gookit:masterfrom
dualfroz:dualfroz/rgbtohex-zeropad
Sep 7, 2026
Merged

inhere merged 1 commit into
gookit:masterfrom
dualfroz:dualfroz/rgbtohex-zeropad

Conversation

@dualfroz

@dualfroz dualfroz commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Problem

RgbToHex (and its alias Rgb2hex) does not zero-pad channel values below
0x10, so a single hex digit is emitted for those channels:

color.RgbToHex([]int{1, 2, 3})   // "123"    , want "010203"
color.RgbToHex([]int{0, 0, 0})   // "000"    , want "000000"
color.RgbToHex([]int{10, 11, 12}) // "0a0b0c" -> actually "abc"

This breaks two contracts:

  1. The round-trip with HexToRgb is no longer an identity for colors with any
    channel below 16:

    color.RgbToHex(color.HexToRgb("010203")) // "123", not "010203"
  2. Rgb2basic looks a color up in hex2basicMap, whose keys are all
    fixed-width 6-digit hex strings (e.g. "000000"). Because RgbToHex(0,0,0)
    returns "000", pure black never matches the fast-path map and silently falls
    through to the approximate RgbToAnsi.

The sibling converter RgbTo256 already builds its hex key with
fmt.Sprintf("%02x%02x%02x", ...), so this is an inconsistency within the same
file.

Root cause

convert.go, RgbToHex:

for _, v := range rgb {
	hexNodes = append(hexNodes, strconv.FormatInt(int64(v), 16))
}

strconv.FormatInt(_, 16) produces the minimal representation, i.e. one digit
for values 0-15.

Fix

Format each channel as a fixed two-digit hex value:

hexNodes = append(hexNodes, fmt.Sprintf("%02x", v))

fmt is already imported in the file. Existing values >= 16 are unchanged.

Test

Extended TestRgbToHex in utils_test.go with small-channel cases
({0,0,0}, {1,2,3}, {10,11,12}) and an explicit
RgbToHex(HexToRgb(hex)) == hex round-trip check.

  • Gate: go test ./ -> ok (PASS)
  • go vet ./... -> exit 0
  • go build ./... -> exit 0

Counterfactual: reverting RgbToHex to strconv.FormatInt(int64(v), 16) makes
the new test fail with expect: "abc" vs actual: "0a0b0c".

Note: gofmt -l convert.go reports the file, but this is pre-existing (a doc
comment near line 866, unrelated to this change and present on the base commit).
The changed region is gofmt-clean, so unrelated lines were intentionally left
untouched.

RgbToHex emitted a single hex digit for channel values below 0x10 because
strconv.FormatInt produces the minimal representation. This broke the
fixed-width hex format, the round-trip with HexToRgb, and the hex2basicMap
fast path in Rgb2basic. Format each channel with %02x, matching RgbTo256.
@dualfroz
dualfroz force-pushed the dualfroz/rgbtohex-zeropad branch from c6ab3d8 to 3be6cf3 Compare September 5, 2026 22:44
@inhere
inhere merged commit 0d17892 into gookit:master Sep 7, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants