...

Source file src/image/png/writer_test.go

Documentation: image/png

		 1  // Copyright 2009 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 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  	// The filenames variable is declared in reader_test.go.
		50  	names := filenames
		51  	if testing.Short() {
		52  		names = filenamesShort
		53  	}
		54  	for _, fn := range names {
		55  		qfn := "testdata/pngsuite/" + fn + ".png"
		56  		// Read the image.
		57  		m0, err := readPNG(qfn)
		58  		if err != nil {
		59  			t.Error(fn, err)
		60  			continue
		61  		}
		62  		// Read the image again, encode it, and decode it.
		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  		// Compare the two.
		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  			// Create a paletted image with the correct palette length
	 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  			// Encode the image
	 145  			var b bytes.Buffer
	 146  			if err := Encode(&b, m0); err != nil {
	 147  				t.Error(err)
	 148  				return
	 149  			}
	 150  			const chunkFieldsLength = 12 // 4 bytes for length, name and crc
	 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  					// Uncompress the image data
	 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  	// Set all pixels to 0xFF alpha to force opaque mode.
	 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  	// Set all pixels to 0xFF alpha to force opaque mode.
	 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