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