github.com/ccccaoqing/test@v0.0.0-20220510085219-3985d23445c0/src/image/png/reader_test.go (about)

     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  	"bufio"
     9  	"fmt"
    10  	"image"
    11  	"image/color"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  var filenames = []string{
    20  	"basn0g01",
    21  	"basn0g01-30",
    22  	"basn0g02",
    23  	"basn0g02-29",
    24  	"basn0g04",
    25  	"basn0g04-31",
    26  	"basn0g08",
    27  	"basn0g16",
    28  	"basn2c08",
    29  	"basn2c16",
    30  	"basn3p01",
    31  	"basn3p02",
    32  	"basn3p04",
    33  	"basn3p04-31i",
    34  	"basn3p08",
    35  	"basn3p08-trns",
    36  	"basn4a08",
    37  	"basn4a16",
    38  	"basn6a08",
    39  	"basn6a16",
    40  }
    41  
    42  var filenamesPaletted = []string{
    43  	"basn3p01",
    44  	"basn3p02",
    45  	"basn3p04",
    46  	"basn3p08",
    47  	"basn3p08-trns",
    48  }
    49  
    50  var filenamesShort = []string{
    51  	"basn0g01",
    52  	"basn0g04-31",
    53  	"basn6a16",
    54  }
    55  
    56  func readPNG(filename string) (image.Image, error) {
    57  	f, err := os.Open(filename)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	defer f.Close()
    62  	return Decode(f)
    63  }
    64  
    65  // An approximation of the sng command-line tool.
    66  func sng(w io.WriteCloser, filename string, png image.Image) {
    67  	defer w.Close()
    68  	bounds := png.Bounds()
    69  	cm := png.ColorModel()
    70  	var bitdepth int
    71  	switch cm {
    72  	case color.RGBAModel, color.NRGBAModel, color.AlphaModel, color.GrayModel:
    73  		bitdepth = 8
    74  	default:
    75  		bitdepth = 16
    76  	}
    77  	cpm, _ := cm.(color.Palette)
    78  	var paletted *image.Paletted
    79  	if cpm != nil {
    80  		switch {
    81  		case len(cpm) <= 2:
    82  			bitdepth = 1
    83  		case len(cpm) <= 4:
    84  			bitdepth = 2
    85  		case len(cpm) <= 16:
    86  			bitdepth = 4
    87  		default:
    88  			bitdepth = 8
    89  		}
    90  		paletted = png.(*image.Paletted)
    91  	}
    92  
    93  	// Write the filename and IHDR.
    94  	io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
    95  	fmt.Fprintf(w, "    width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth)
    96  	switch {
    97  	case cm == color.RGBAModel, cm == color.RGBA64Model:
    98  		io.WriteString(w, "    using color;\n")
    99  	case cm == color.NRGBAModel, cm == color.NRGBA64Model:
   100  		io.WriteString(w, "    using color alpha;\n")
   101  	case cm == color.GrayModel, cm == color.Gray16Model:
   102  		io.WriteString(w, "    using grayscale;\n")
   103  	case cpm != nil:
   104  		io.WriteString(w, "    using color palette;\n")
   105  	default:
   106  		io.WriteString(w, "unknown PNG decoder color model\n")
   107  	}
   108  	io.WriteString(w, "}\n")
   109  
   110  	// We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it
   111  	// (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder").
   112  	io.WriteString(w, "gAMA {1.0000}\n")
   113  
   114  	// Write the PLTE and tRNS (if applicable).
   115  	if cpm != nil {
   116  		lastAlpha := -1
   117  		io.WriteString(w, "PLTE {\n")
   118  		for i, c := range cpm {
   119  			var r, g, b, a uint8
   120  			switch c := c.(type) {
   121  			case color.RGBA:
   122  				r, g, b, a = c.R, c.G, c.B, 0xff
   123  			case color.NRGBA:
   124  				r, g, b, a = c.R, c.G, c.B, c.A
   125  			default:
   126  				panic("unknown palette color type")
   127  			}
   128  			if a != 0xff {
   129  				lastAlpha = i
   130  			}
   131  			fmt.Fprintf(w, "    (%3d,%3d,%3d)     # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
   132  		}
   133  		io.WriteString(w, "}\n")
   134  		if lastAlpha != -1 {
   135  			io.WriteString(w, "tRNS {\n")
   136  			for i := 0; i <= lastAlpha; i++ {
   137  				_, _, _, a := cpm[i].RGBA()
   138  				a >>= 8
   139  				fmt.Fprintf(w, " %d", a)
   140  			}
   141  			io.WriteString(w, "}\n")
   142  		}
   143  	}
   144  
   145  	// Write the IMAGE.
   146  	io.WriteString(w, "IMAGE {\n    pixels hex\n")
   147  	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
   148  		switch {
   149  		case cm == color.GrayModel:
   150  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   151  				gray := png.At(x, y).(color.Gray)
   152  				fmt.Fprintf(w, "%02x", gray.Y)
   153  			}
   154  		case cm == color.Gray16Model:
   155  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   156  				gray16 := png.At(x, y).(color.Gray16)
   157  				fmt.Fprintf(w, "%04x ", gray16.Y)
   158  			}
   159  		case cm == color.RGBAModel:
   160  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   161  				rgba := png.At(x, y).(color.RGBA)
   162  				fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B)
   163  			}
   164  		case cm == color.RGBA64Model:
   165  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   166  				rgba64 := png.At(x, y).(color.RGBA64)
   167  				fmt.Fprintf(w, "%04x%04x%04x ", rgba64.R, rgba64.G, rgba64.B)
   168  			}
   169  		case cm == color.NRGBAModel:
   170  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   171  				nrgba := png.At(x, y).(color.NRGBA)
   172  				fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
   173  			}
   174  		case cm == color.NRGBA64Model:
   175  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   176  				nrgba64 := png.At(x, y).(color.NRGBA64)
   177  				fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
   178  			}
   179  		case cpm != nil:
   180  			var b, c int
   181  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   182  				b = b<<uint(bitdepth) | int(paletted.ColorIndexAt(x, y))
   183  				c++
   184  				if c == 8/bitdepth {
   185  					fmt.Fprintf(w, "%02x", b)
   186  					b = 0
   187  					c = 0
   188  				}
   189  			}
   190  			if c != 0 {
   191  				for c != 8/bitdepth {
   192  					b = b << uint(bitdepth)
   193  					c++
   194  				}
   195  				fmt.Fprintf(w, "%02x", b)
   196  			}
   197  		}
   198  		io.WriteString(w, "\n")
   199  	}
   200  	io.WriteString(w, "}\n")
   201  }
   202  
   203  func TestReader(t *testing.T) {
   204  	names := filenames
   205  	if testing.Short() {
   206  		names = filenamesShort
   207  	}
   208  	for _, fn := range names {
   209  		// Read the .png file.
   210  		img, err := readPNG("testdata/pngsuite/" + fn + ".png")
   211  		if err != nil {
   212  			t.Error(fn, err)
   213  			continue
   214  		}
   215  
   216  		if fn == "basn4a16" {
   217  			// basn4a16.sng is gray + alpha but sng() will produce true color + alpha
   218  			// so we just check a single random pixel.
   219  			c := img.At(2, 1).(color.NRGBA64)
   220  			if c.R != 0x11a7 || c.G != 0x11a7 || c.B != 0x11a7 || c.A != 0x1085 {
   221  				t.Error(fn, fmt.Errorf("wrong pixel value at (2, 1): %x", c))
   222  			}
   223  			continue
   224  		}
   225  
   226  		piper, pipew := io.Pipe()
   227  		pb := bufio.NewScanner(piper)
   228  		go sng(pipew, fn, img)
   229  		defer piper.Close()
   230  
   231  		// Read the .sng file.
   232  		sf, err := os.Open("testdata/pngsuite/" + fn + ".sng")
   233  		if err != nil {
   234  			t.Error(fn, err)
   235  			continue
   236  		}
   237  		defer sf.Close()
   238  		sb := bufio.NewScanner(sf)
   239  		if err != nil {
   240  			t.Error(fn, err)
   241  			continue
   242  		}
   243  
   244  		// Compare the two, in SNG format, line by line.
   245  		for {
   246  			pdone := !pb.Scan()
   247  			sdone := !sb.Scan()
   248  			if pdone && sdone {
   249  				break
   250  			}
   251  			if pdone || sdone {
   252  				t.Errorf("%s: Different sizes", fn)
   253  				break
   254  			}
   255  			ps := pb.Text()
   256  			ss := sb.Text()
   257  			if ps != ss {
   258  				t.Errorf("%s: Mismatch\n%sversus\n%s\n", fn, ps, ss)
   259  				break
   260  			}
   261  		}
   262  		if pb.Err() != nil {
   263  			t.Error(fn, pb.Err())
   264  		}
   265  		if sb.Err() != nil {
   266  			t.Error(fn, sb.Err())
   267  		}
   268  	}
   269  }
   270  
   271  var readerErrors = []struct {
   272  	file string
   273  	err  string
   274  }{
   275  	{"invalid-zlib.png", "zlib: invalid checksum"},
   276  	{"invalid-crc32.png", "invalid checksum"},
   277  	{"invalid-noend.png", "unexpected EOF"},
   278  	{"invalid-trunc.png", "unexpected EOF"},
   279  }
   280  
   281  func TestReaderError(t *testing.T) {
   282  	for _, tt := range readerErrors {
   283  		img, err := readPNG("testdata/" + tt.file)
   284  		if err == nil {
   285  			t.Errorf("decoding %s: missing error", tt.file)
   286  			continue
   287  		}
   288  		if !strings.Contains(err.Error(), tt.err) {
   289  			t.Errorf("decoding %s: %s, want %s", tt.file, err, tt.err)
   290  		}
   291  		if img != nil {
   292  			t.Errorf("decoding %s: have image + error", tt.file)
   293  		}
   294  	}
   295  }
   296  
   297  func TestPalettedDecodeConfig(t *testing.T) {
   298  	for _, fn := range filenamesPaletted {
   299  		f, err := os.Open("testdata/pngsuite/" + fn + ".png")
   300  		if err != nil {
   301  			t.Errorf("%s: open failed: %v", fn, err)
   302  			continue
   303  		}
   304  		defer f.Close()
   305  		cfg, err := DecodeConfig(f)
   306  		if err != nil {
   307  			t.Errorf("%s: %v", fn, err)
   308  			continue
   309  		}
   310  		pal, ok := cfg.ColorModel.(color.Palette)
   311  		if !ok {
   312  			t.Errorf("%s: expected paletted color model", fn)
   313  			continue
   314  		}
   315  		if pal == nil {
   316  			t.Errorf("%s: palette not initialized", fn)
   317  			continue
   318  		}
   319  	}
   320  }
   321  
   322  func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) {
   323  	b.StopTimer()
   324  	data, err := ioutil.ReadFile(filename)
   325  	if err != nil {
   326  		b.Fatal(err)
   327  	}
   328  	s := string(data)
   329  	cfg, err := DecodeConfig(strings.NewReader(s))
   330  	if err != nil {
   331  		b.Fatal(err)
   332  	}
   333  	b.SetBytes(int64(cfg.Width * cfg.Height * bytesPerPixel))
   334  	b.StartTimer()
   335  	for i := 0; i < b.N; i++ {
   336  		Decode(strings.NewReader(s))
   337  	}
   338  }
   339  
   340  func BenchmarkDecodeGray(b *testing.B) {
   341  	benchmarkDecode(b, "testdata/benchGray.png", 1)
   342  }
   343  
   344  func BenchmarkDecodeNRGBAGradient(b *testing.B) {
   345  	benchmarkDecode(b, "testdata/benchNRGBA-gradient.png", 4)
   346  }
   347  
   348  func BenchmarkDecodeNRGBAOpaque(b *testing.B) {
   349  	benchmarkDecode(b, "testdata/benchNRGBA-opaque.png", 4)
   350  }
   351  
   352  func BenchmarkDecodePaletted(b *testing.B) {
   353  	benchmarkDecode(b, "testdata/benchPaletted.png", 1)
   354  }
   355  
   356  func BenchmarkDecodeRGB(b *testing.B) {
   357  	benchmarkDecode(b, "testdata/benchRGB.png", 4)
   358  }
   359  
   360  func BenchmarkDecodeInterlacing(b *testing.B) {
   361  	benchmarkDecode(b, "testdata/benchRGB-interlace.png", 4)
   362  }