Skip to content

feat(texconv): add texture format decoders and sRGB fixes#17

Merged
thesprockee merged 1 commit intomainfrom
feat/texture-decoders
Mar 14, 2026
Merged

feat(texconv): add texture format decoders and sRGB fixes#17
thesprockee merged 1 commit intomainfrom
feat/texture-decoders

Conversation

@thesprockee
Copy link
Member

Summary

  • Add decoders for R8_UNORM, R8G8B8A8, B8G8R8A8 (sRGB/Typeless), R11G11B10_FLOAT
  • Fix RGB565 bit expansion (bit-replication instead of lossy multiply-divide)
  • Add sRGB linear-space interpolation for BC1/BC3 decompression
  • Change output from image.RGBA to image.NRGBA (correct for non-premultiplied alpha)
  • Add IEEE 754 partial-precision float helpers (f11, f10)

Based on work by @heisthecat31 in PR #5, extracted and cleaned up with tests.

Test plan

  • All new decoders tested (R8, RGBA, BGRA, R11G11B10Float)
  • Truncated input validation tested for all decoders
  • f11ToF32 / f10ToF32 tested (zero, one, infinity)
  • sRGB round-trip tested (all 256 values within ±1)
  • BC1 block decompression tested
  • NRGBA return type verified
  • 16 tests, all passing

🤖 Generated with Claude Code

…RGBA output

Add decoders for R8_UNORM (grayscale), R8G8B8A8, B8G8R8A8 (sRGB and
Typeless), and R11G11B10_FLOAT packed format with IEEE partial-precision
float conversion.

Fix RGB565 bit expansion to use proper bit-replication instead of
the lossy multiply-divide method. Add sRGB linear-space interpolation
for BC1/BC3 decompression (correct per DDS spec). Change output image
type from image.RGBA (premultiplied) to image.NRGBA (non-premultiplied),
which is correct for texture data with alpha channels.

Based on work by heisthecat31 in PR #5, cleaned up and tested.

Co-Authored-By: he_is_the_cat <heisthecat31@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 14, 2026 19:24
@thesprockee thesprockee merged commit 7d64bc9 into main Mar 14, 2026
2 checks passed
@thesprockee thesprockee deleted the feat/texture-decoders branch March 14, 2026 19:24
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends texconv’s DDS decoding capabilities by adding support for additional uncompressed DXGI formats and improving correctness for sRGB handling and channel/alpha representation during decompression.

Changes:

  • Added decoders for R8_UNORM, RGBA/BGRA uncompressed formats (including sRGB/typeless variants), and R11G11B10_FLOAT.
  • Fixed RGB565 expansion via bit replication and added linear-space interpolation for BC1/BC3 sRGB decompression paths.
  • Switched decode output image type from image.RGBA to image.NRGBA and added helper conversions/tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
cmd/texconv/main.go Adds new format constants, mip sizing for new formats, decoders (R8/RGBA/BGRA/R11G11B10Float), sRGB helpers, and updates BC1/BC3 to optionally interpolate in linear space; changes decode output to NRGBA.
cmd/texconv/decode_test.go Introduces unit tests for new decoders and float helpers, plus some basic BC1 and sRGB round-trip coverage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +607 to +615
if isSRGB {
// sRGB linear-space interpolation
lr0, lg0, lb0 := srgbToLinear(r0_8), srgbToLinear(g0_8), srgbToLinear(b0_8)
lr1, lg1, lb1 := srgbToLinear(r1_8), srgbToLinear(g1_8), srgbToLinear(b1_8)
colors[2] = [4]uint8{
linearToSrgb((2*lr0 + lr1) / 3),
linearToSrgb((2*lg0 + lg1) / 3),
linearToSrgb((2*lb0 + lb1) / 3),
255,
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := f10ToF32(tt.in)
if math.Abs(float64(got-tt.want)) > 0.001 {
Comment on lines +216 to +225
func TestDecompressBC1_NRGBA(t *testing.T) {
// Verify return type is NRGBA, not RGBA
block := make([]byte, 8)
img, err := decompressBC1(block, 4, 4, false)
if err != nil {
t.Fatal(err)
}
// Type assertion — img should be *image.NRGBA
_ = img.Stride // only NRGBA has Stride; compilation proves the type
}

// decompressBC decompresses BC-compressed data to RGBA
func decompressBC(data []byte, info *TextureInfo) (*image.RGBA, error) {
// decompressBC decompresses BC-compressed data to NRGBA
Comment on lines +881 to +884
if v <= 0.0031308 {
return uint8(min(255, max(0, float64(v)*12.92*255.0)))
}
srgb := 1.055*math.Pow(float64(v), 1.0/2.4) - 0.055
Comment on lines +957 to +967
// Clamp to [0,1] and convert to uint8
dstOff := nrgba.PixOffset(x, y)
nrgba.Pix[dstOff+0] = uint8(min(255, max(0, float64(r)*255.0)))
nrgba.Pix[dstOff+1] = uint8(min(255, max(0, float64(g)*255.0)))
nrgba.Pix[dstOff+2] = uint8(min(255, max(0, float64(b)*255.0)))
nrgba.Pix[dstOff+3] = 255
}
}
return nrgba, nil
}

return nrgba, nil
}

// decompressBC5 decompresses BC5 (normal maps) to RGBA
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