Source file
src/image/png/writer_test.go
1
2
3
4
5 package png
6
7 import (
8 "bytes"
9 "compress/zlib"
10 "encoding/binary"
11 "fmt"
12 "image"
13 "image/color"
14 "io"
15 "testing"
16 )
17
18 func diff(m0, m1 image.Image) error {
19 b0, b1 := m0.Bounds(), m1.Bounds()
20 if !b0.Size().Eq(b1.Size()) {
21 return fmt.Errorf("dimensions differ: %v vs %v", b0, b1)
22 }
23 dx := b1.Min.X - b0.Min.X
24 dy := b1.Min.Y - b0.Min.Y
25 for y := b0.Min.Y; y < b0.Max.Y; y++ {
26 for x := b0.Min.X; x < b0.Max.X; x++ {
27 c0 := m0.At(x, y)
28 c1 := m1.At(x+dx, y+dy)
29 r0, g0, b0, a0 := c0.RGBA()
30 r1, g1, b1, a1 := c1.RGBA()
31 if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
32 return fmt.Errorf("colors differ at (%d, %d): %v vs %v", x, y, c0, c1)
33 }
34 }
35 }
36 return nil
37 }
38
39 func encodeDecode(m image.Image) (image.Image, error) {
40 var b bytes.Buffer
41 err := Encode(&b, m)
42 if err != nil {
43 return nil, err
44 }
45 return Decode(&b)
46 }
47
48 func TestWriter(t *testing.T) {
49
50 names := filenames
51 if testing.Short() {
52 names = filenamesShort
53 }
54 for _, fn := range names {
55 qfn := "testdata/pngsuite/" + fn + ".png"
56
57 m0, err := readPNG(qfn)
58 if err != nil {
59 t.Error(fn, err)
60 continue
61 }
62
63 m1, err := readPNG(qfn)
64 if err != nil {
65 t.Error(fn, err)
66 continue
67 }
68 m2, err := encodeDecode(m1)
69 if err != nil {
70 t.Error(fn, err)
71 continue
72 }
73
74 err = diff(m0, m2)
75 if err != nil {
76 t.Error(fn, err)
77 continue
78 }
79 }
80 }
81
82 func TestWriterPaletted(t *testing.T) {
83 const width, height = 32, 16
84
85 testCases := []struct {
86 plen int
87 bitdepth uint8
88 datalen int
89 }{
90
91 {
92 plen: 256,
93 bitdepth: 8,
94 datalen: (1 + width) * height,
95 },
96
97 {
98 plen: 128,
99 bitdepth: 8,
100 datalen: (1 + width) * height,
101 },
102
103 {
104 plen: 16,
105 bitdepth: 4,
106 datalen: (1 + width/2) * height,
107 },
108
109 {
110 plen: 4,
111 bitdepth: 2,
112 datalen: (1 + width/4) * height,
113 },
114
115 {
116 plen: 2,
117 bitdepth: 1,
118 datalen: (1 + width/8) * height,
119 },
120 }
121
122 for _, tc := range testCases {
123 t.Run(fmt.Sprintf("plen-%d", tc.plen), func(t *testing.T) {
124
125 palette := make(color.Palette, tc.plen)
126 for i := range palette {
127 palette[i] = color.NRGBA{
128 R: uint8(i),
129 G: uint8(i),
130 B: uint8(i),
131 A: 255,
132 }
133 }
134 m0 := image.NewPaletted(image.Rect(0, 0, width, height), palette)
135
136 i := 0
137 for y := 0; y < height; y++ {
138 for x := 0; x < width; x++ {
139 m0.SetColorIndex(x, y, uint8(i%tc.plen))
140 i++
141 }
142 }
143
144
145 var b bytes.Buffer
146 if err := Encode(&b, m0); err != nil {
147 t.Error(err)
148 return
149 }
150 const chunkFieldsLength = 12
151 data := b.Bytes()
152 i = len(pngHeader)
153
154 for i < len(data)-chunkFieldsLength {
155 length := binary.BigEndian.Uint32(data[i : i+4])
156 name := string(data[i+4 : i+8])
157
158 switch name {
159 case "IHDR":
160 bitdepth := data[i+8+8]
161 if bitdepth != tc.bitdepth {
162 t.Errorf("got bitdepth %d, want %d", bitdepth, tc.bitdepth)
163 }
164 case "IDAT":
165
166 r, err := zlib.NewReader(bytes.NewReader(data[i+8 : i+8+int(length)]))
167 if err != nil {
168 t.Error(err)
169 return
170 }
171 n, err := io.Copy(io.Discard, r)
172 if err != nil {
173 t.Errorf("got error while reading image data: %v", err)
174 }
175 if n != int64(tc.datalen) {
176 t.Errorf("got uncompressed data length %d, want %d", n, tc.datalen)
177 }
178 }
179
180 i += chunkFieldsLength + int(length)
181 }
182 })
183
184 }
185 }
186
187 func TestWriterLevels(t *testing.T) {
188 m := image.NewNRGBA(image.Rect(0, 0, 100, 100))
189
190 var b1, b2 bytes.Buffer
191 if err := (&Encoder{}).Encode(&b1, m); err != nil {
192 t.Fatal(err)
193 }
194 noenc := &Encoder{CompressionLevel: NoCompression}
195 if err := noenc.Encode(&b2, m); err != nil {
196 t.Fatal(err)
197 }
198
199 if b2.Len() <= b1.Len() {
200 t.Error("DefaultCompression encoding was larger than NoCompression encoding")
201 }
202 if _, err := Decode(&b1); err != nil {
203 t.Error("cannot decode DefaultCompression")
204 }
205 if _, err := Decode(&b2); err != nil {
206 t.Error("cannot decode NoCompression")
207 }
208 }
209
210 func TestSubImage(t *testing.T) {
211 m0 := image.NewRGBA(image.Rect(0, 0, 256, 256))
212 for y := 0; y < 256; y++ {
213 for x := 0; x < 256; x++ {
214 m0.Set(x, y, color.RGBA{uint8(x), uint8(y), 0, 255})
215 }
216 }
217 m0 = m0.SubImage(image.Rect(50, 30, 250, 130)).(*image.RGBA)
218 m1, err := encodeDecode(m0)
219 if err != nil {
220 t.Error(err)
221 return
222 }
223 err = diff(m0, m1)
224 if err != nil {
225 t.Error(err)
226 return
227 }
228 }
229
230 func BenchmarkEncodeGray(b *testing.B) {
231 img := image.NewGray(image.Rect(0, 0, 640, 480))
232 b.SetBytes(640 * 480 * 1)
233 b.ReportAllocs()
234 b.ResetTimer()
235 for i := 0; i < b.N; i++ {
236 Encode(io.Discard, img)
237 }
238 }
239
240 type pool struct {
241 b *EncoderBuffer
242 }
243
244 func (p *pool) Get() *EncoderBuffer {
245 return p.b
246 }
247
248 func (p *pool) Put(b *EncoderBuffer) {
249 p.b = b
250 }
251
252 func BenchmarkEncodeGrayWithBufferPool(b *testing.B) {
253 img := image.NewGray(image.Rect(0, 0, 640, 480))
254 e := Encoder{
255 BufferPool: &pool{},
256 }
257 b.SetBytes(640 * 480 * 1)
258 b.ReportAllocs()
259 b.ResetTimer()
260 for i := 0; i < b.N; i++ {
261 e.Encode(io.Discard, img)
262 }
263 }
264
265 func BenchmarkEncodeNRGBOpaque(b *testing.B) {
266 img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
267
268 bo := img.Bounds()
269 for y := bo.Min.Y; y < bo.Max.Y; y++ {
270 for x := bo.Min.X; x < bo.Max.X; x++ {
271 img.Set(x, y, color.NRGBA{0, 0, 0, 255})
272 }
273 }
274 if !img.Opaque() {
275 b.Fatal("expected image to be opaque")
276 }
277 b.SetBytes(640 * 480 * 4)
278 b.ReportAllocs()
279 b.ResetTimer()
280 for i := 0; i < b.N; i++ {
281 Encode(io.Discard, img)
282 }
283 }
284
285 func BenchmarkEncodeNRGBA(b *testing.B) {
286 img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
287 if img.Opaque() {
288 b.Fatal("expected image not to be opaque")
289 }
290 b.SetBytes(640 * 480 * 4)
291 b.ReportAllocs()
292 b.ResetTimer()
293 for i := 0; i < b.N; i++ {
294 Encode(io.Discard, img)
295 }
296 }
297
298 func BenchmarkEncodePaletted(b *testing.B) {
299 img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{
300 color.RGBA{0, 0, 0, 255},
301 color.RGBA{255, 255, 255, 255},
302 })
303 b.SetBytes(640 * 480 * 1)
304 b.ReportAllocs()
305 b.ResetTimer()
306 for i := 0; i < b.N; i++ {
307 Encode(io.Discard, img)
308 }
309 }
310
311 func BenchmarkEncodeRGBOpaque(b *testing.B) {
312 img := image.NewRGBA(image.Rect(0, 0, 640, 480))
313
314 bo := img.Bounds()
315 for y := bo.Min.Y; y < bo.Max.Y; y++ {
316 for x := bo.Min.X; x < bo.Max.X; x++ {
317 img.Set(x, y, color.RGBA{0, 0, 0, 255})
318 }
319 }
320 if !img.Opaque() {
321 b.Fatal("expected image to be opaque")
322 }
323 b.SetBytes(640 * 480 * 4)
324 b.ReportAllocs()
325 b.ResetTimer()
326 for i := 0; i < b.N; i++ {
327 Encode(io.Discard, img)
328 }
329 }
330
331 func BenchmarkEncodeRGBA(b *testing.B) {
332 img := image.NewRGBA(image.Rect(0, 0, 640, 480))
333 if img.Opaque() {
334 b.Fatal("expected image not to be opaque")
335 }
336 b.SetBytes(640 * 480 * 4)
337 b.ReportAllocs()
338 b.ResetTimer()
339 for i := 0; i < b.N; i++ {
340 Encode(io.Discard, img)
341 }
342 }
343
View as plain text