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 }