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 }