Skip to content

Commit bd8a829

Browse files
committed
fix: DecodePaletteOrErr does not return an error when the number of colors is exceeded
DecodePaletteOrErr does not return an error when the number of colors is exceeded for images with RGB color mode. For indexed palette the behavior is correct.
1 parent 6975592 commit bd8a829

File tree

9 files changed

+83
-6
lines changed

9 files changed

+83
-6
lines changed

internal/palette.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type PaletteMaker[T ~uint32] struct { // generic type used instead of pi.RGB to
1515
index int
1616
}
1717

18-
// Add returns true, when there is no space left
18+
// Add returns true, when the number of colors has been exceeded
1919
func (p *PaletteMaker[T]) Add(c color.Color) bool {
2020
if p.colorAlreadyUsed == nil {
2121
p.colorAlreadyUsed = map[color.Color]struct{}{}
@@ -24,6 +24,8 @@ func (p *PaletteMaker[T]) Add(c color.Color) bool {
2424
_, found := p.colorAlreadyUsed[c]
2525
if found {
2626
return false
27+
} else if p.index == maxColors {
28+
return true
2729
}
2830

2931
p.colorAlreadyUsed[c] = struct{}{}
@@ -36,7 +38,7 @@ func (p *PaletteMaker[T]) Add(c color.Color) bool {
3638

3739
p.index++
3840

39-
return p.index == maxColors
41+
return false
4042
}
4143

4244
func (p *PaletteMaker[T]) Palette() [maxColors]T {
284 Bytes
Loading
287 Bytes
Loading

internal/test/palette/indexed.png

104 Bytes
Loading

internal/test/palette/rgb-64.png

274 Bytes
Loading

internal/test/palette/rgb-65.png

277 Bytes
Loading

internal/test/palette/rgb.png

91 Bytes
Loading

pallette.go renamed to palette.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ func defaultPalette() PaletteArray {
125125
}
126126
}
127127

128+
var errToManyColors = fmt.Errorf(
129+
"PNG file has too many colors in indexed palette. "+
130+
"The maximum number is %d", MaxColors)
131+
128132
// DecodePalette extracts a palette from a PNG file.
129133
//
130134
// - If the file uses an indexed palette, colors are read directly
@@ -150,9 +154,7 @@ func DecodePaletteOrErr(pngFile []byte) (PaletteArray, error) {
150154

151155
if indexedPalette, ok := stdImage.ColorModel().(color.Palette); ok {
152156
if len(indexedPalette) > MaxColors {
153-
return PaletteArray{},
154-
fmt.Errorf("PNG file has too many colors in indexed palette. "+
155-
"The maximum number is %d", MaxColors)
157+
return PaletteArray{}, errToManyColors
156158
}
157159
return convertIndexedPaletteToRGB(indexedPalette), nil
158160
}
@@ -164,7 +166,7 @@ func DecodePaletteOrErr(pngFile []byte) (PaletteArray, error) {
164166
for x := 0; x < bounds.Max.X; x++ {
165167
c := stdImage.At(x, y)
166168
if palette.Add(c) {
167-
return palette.Palette(), nil
169+
return PaletteArray{}, errToManyColors
168170
}
169171
}
170172
}

palette_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2025 Jacek Olszak
2+
// This code is licensed under MIT license (see LICENSE for details)
3+
4+
package pi_test
5+
6+
import (
7+
_ "embed"
8+
"github.com/elgopher/pi"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
"testing"
12+
)
13+
14+
var (
15+
//go:embed internal/test/palette/indexed.png
16+
indexedPalettePNG []byte
17+
//go:embed internal/test/palette/rgb.png
18+
rgbPalettePNG []byte
19+
20+
//go:embed internal/test/palette/indexed-65.png
21+
indexed65PalettePNG []byte
22+
//go:embed internal/test/palette/rgb-65.png
23+
rgb65PalettePNG []byte
24+
25+
//go:embed internal/test/palette/indexed-64.png
26+
indexed64PalettePNG []byte
27+
//go:embed internal/test/palette/rgb-64.png
28+
rgb64PalettePNG []byte
29+
)
30+
31+
func TestDecodePaletteOrErr(t *testing.T) {
32+
t.Run("should return error when palette has too many colors", func(t *testing.T) {
33+
tests := map[string][]byte{
34+
"indexed": indexed65PalettePNG,
35+
"rgb": rgb65PalettePNG,
36+
}
37+
for testName, file := range tests {
38+
t.Run(testName, func(t *testing.T) {
39+
palette, err := pi.DecodePaletteOrErr(file)
40+
require.ErrorContains(t, err, "too many colors")
41+
assert.Zero(t, palette)
42+
})
43+
}
44+
})
45+
46+
t.Run("should not return error when palette has maximum number of colors", func(t *testing.T) {
47+
tests := map[string][]byte{
48+
"indexed": indexed64PalettePNG,
49+
"rgb": rgb64PalettePNG,
50+
}
51+
for testName, file := range tests {
52+
t.Run(testName, func(t *testing.T) {
53+
_, err := pi.DecodePaletteOrErr(file)
54+
assert.NoError(t, err)
55+
})
56+
}
57+
})
58+
59+
t.Run("should decode palette", func(t *testing.T) {
60+
expected := pi.PaletteArray{0x000000, 0x1900ff, 0xff00b0, 0xff2600}
61+
tests := map[string][]byte{
62+
"indexed": indexedPalettePNG,
63+
"rgb": rgbPalettePNG,
64+
}
65+
for testName, file := range tests {
66+
t.Run(testName, func(t *testing.T) {
67+
palette, err := pi.DecodePaletteOrErr(file)
68+
require.NoError(t, err)
69+
assert.Equal(t, expected, palette)
70+
})
71+
}
72+
})
73+
}

0 commit comments

Comments
 (0)