diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go index e2d679bb42..eee4eac2e1 100644 --- a/src/pkg/image/png/reader.go +++ b/src/pkg/image/png/reader.go @@ -29,11 +29,19 @@ const ( // A cb is a combination of color type and bit depth. const ( cbInvalid = iota + cbG1 + cbG2 + cbG4 cbG8 + cbGA8 cbTC8 + cbP1 + cbP2 + cbP4 cbP8 cbTCA8 cbG16 + cbGA16 cbTC16 cbTCA16 ) @@ -70,6 +78,7 @@ type imgOrErr struct { type decoder struct { width, height int + depth int palette image.PalettedColorModel cb int stage int @@ -138,7 +147,29 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro return UnsupportedError("dimension overflow") } d.cb = cbInvalid - switch d.tmp[8] { + d.depth = int(d.tmp[8]) + switch d.depth { + case 1: + switch d.tmp[9] { + case ctGrayscale: + d.cb = cbG1 + case ctPaletted: + d.cb = cbP1 + } + case 2: + switch d.tmp[9] { + case ctGrayscale: + d.cb = cbG2 + case ctPaletted: + d.cb = cbP2 + } + case 4: + switch d.tmp[9] { + case ctGrayscale: + d.cb = cbG4 + case ctPaletted: + d.cb = cbP4 + } case 8: switch d.tmp[9] { case ctGrayscale: @@ -147,6 +178,8 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro d.cb = cbTC8 case ctPaletted: d.cb = cbP8 + case ctGrayscaleAlpha: + d.cb = cbGA8 case ctTrueColorAlpha: d.cb = cbTCA8 } @@ -156,6 +189,8 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro d.cb = cbG16 case ctTrueColor: d.cb = cbTC16 + case ctGrayscaleAlpha: + d.cb = cbGA16 case ctTrueColorAlpha: d.cb = cbTCA16 } @@ -169,7 +204,7 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Error { np := int(length / 3) // The number of palette entries. - if length%3 != 0 || np <= 0 || np > 256 { + if length%3 != 0 || np <= 0 || np > 256 || np > 1< len(d.palette) { return FormatError("bad tRNS length") } @@ -214,7 +249,7 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro rgba := d.palette[i].(image.RGBAColor) d.palette[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]} } - case cbTCA8, cbTCA16: + case cbGA8, cbGA16, cbTCA8, cbTCA16: return FormatError("tRNS, color type mismatch") } return nil @@ -240,7 +275,7 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { return nil, err } defer r.Close() - bpp := 0 // Bytes per pixel. + bitsPerPixel := 0 maxPalette := uint8(0) var ( gray *image.Gray @@ -253,40 +288,50 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { img image.Image ) switch d.cb { - case cbG8: - bpp = 1 + case cbG1, cbG2, cbG4, cbG8: + bitsPerPixel = d.depth gray = image.NewGray(d.width, d.height) img = gray + case cbGA8: + bitsPerPixel = 16 + nrgba = image.NewNRGBA(d.width, d.height) + img = nrgba case cbTC8: - bpp = 3 + bitsPerPixel = 24 rgba = image.NewRGBA(d.width, d.height) img = rgba - case cbP8: - bpp = 1 + case cbP1, cbP2, cbP4, cbP8: + bitsPerPixel = d.depth paletted = image.NewPaletted(d.width, d.height, d.palette) img = paletted maxPalette = uint8(len(d.palette) - 1) case cbTCA8: - bpp = 4 + bitsPerPixel = 32 nrgba = image.NewNRGBA(d.width, d.height) img = nrgba case cbG16: - bpp = 2 + bitsPerPixel = 16 gray16 = image.NewGray16(d.width, d.height) img = gray16 + case cbGA16: + bitsPerPixel = 32 + nrgba64 = image.NewNRGBA64(d.width, d.height) + img = nrgba64 case cbTC16: - bpp = 6 + bitsPerPixel = 48 rgba64 = image.NewRGBA64(d.width, d.height) img = rgba64 case cbTCA16: - bpp = 8 + bitsPerPixel = 64 nrgba64 = image.NewNRGBA64(d.width, d.height) img = nrgba64 } + bytesPerPixel := (bitsPerPixel + 7) / 8 + // cr and pr are the bytes for the current and previous row. // The +1 is for the per-row filter type, which is at cr[0]. - cr := make([]uint8, 1+bpp*d.width) - pr := make([]uint8, 1+bpp*d.width) + cr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8) + pr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8) for y := 0; y < d.height; y++ { // Read the decompressed bytes. @@ -302,26 +347,26 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { case ftNone: // No-op. case ftSub: - for i := bpp; i < len(cdat); i++ { - cdat[i] += cdat[i-bpp] + for i := bytesPerPixel; i < len(cdat); i++ { + cdat[i] += cdat[i-bytesPerPixel] } case ftUp: for i := 0; i < len(cdat); i++ { cdat[i] += pdat[i] } case ftAverage: - for i := 0; i < bpp; i++ { + for i := 0; i < bytesPerPixel; i++ { cdat[i] += pdat[i] / 2 } - for i := bpp; i < len(cdat); i++ { - cdat[i] += uint8((int(cdat[i-bpp]) + int(pdat[i])) / 2) + for i := bytesPerPixel; i < len(cdat); i++ { + cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2) } case ftPaeth: - for i := 0; i < bpp; i++ { + for i := 0; i < bytesPerPixel; i++ { cdat[i] += paeth(0, pdat[i], 0) } - for i := bpp; i < len(cdat); i++ { - cdat[i] += paeth(cdat[i-bpp], pdat[i], pdat[i-bpp]) + for i := bytesPerPixel; i < len(cdat); i++ { + cdat[i] += paeth(cdat[i-bytesPerPixel], pdat[i], pdat[i-bytesPerPixel]) } default: return nil, FormatError("bad filter type") @@ -329,14 +374,79 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { // Convert from bytes to colors. switch d.cb { + case cbG1: + for x := 0; x < d.width; x += 8 { + b := cdat[x/8] + for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ { + gray.Set(x+x2, y, image.GrayColor{(b >> 7) * 0xff}) + b <<= 1 + } + } + case cbG2: + for x := 0; x < d.width; x += 4 { + b := cdat[x/4] + for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ { + gray.Set(x+x2, y, image.GrayColor{(b >> 6) * 0x55}) + b <<= 2 + } + } + case cbG4: + for x := 0; x < d.width; x += 2 { + b := cdat[x/2] + for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ { + gray.Set(x+x2, y, image.GrayColor{(b >> 4) * 0x11}) + b <<= 4 + } + } case cbG8: for x := 0; x < d.width; x++ { gray.Set(x, y, image.GrayColor{cdat[x]}) } + case cbGA8: + for x := 0; x < d.width; x++ { + ycol := cdat[2*x+0] + nrgba.Set(x, y, image.NRGBAColor{ycol, ycol, ycol, cdat[2*x+1]}) + } case cbTC8: for x := 0; x < d.width; x++ { rgba.Set(x, y, image.RGBAColor{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff}) } + case cbP1: + for x := 0; x < d.width; x += 8 { + b := cdat[x/8] + for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ { + idx := b >> 7 + if idx > maxPalette { + return nil, FormatError("palette index out of range") + } + paletted.SetColorIndex(x+x2, y, idx) + b <<= 1 + } + } + case cbP2: + for x := 0; x < d.width; x += 4 { + b := cdat[x/4] + for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ { + idx := b >> 6 + if idx > maxPalette { + return nil, FormatError("palette index out of range") + } + paletted.SetColorIndex(x+x2, y, idx) + b <<= 2 + } + } + case cbP4: + for x := 0; x < d.width; x += 2 { + b := cdat[x/2] + for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ { + idx := b >> 4 + if idx > maxPalette { + return nil, FormatError("palette index out of range") + } + paletted.SetColorIndex(x+x2, y, idx) + b <<= 4 + } + } case cbP8: for x := 0; x < d.width; x++ { if cdat[x] > maxPalette { @@ -353,6 +463,12 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) gray16.Set(x, y, image.Gray16Color{ycol}) } + case cbGA16: + for x := 0; x < d.width; x++ { + ycol := uint16(cdat[4*x+0])<<8 | uint16(cdat[4*x+1]) + acol := uint16(cdat[4*x+2])<<8 | uint16(cdat[4*x+3]) + nrgba64.Set(x, y, image.NRGBA64Color{ycol, ycol, ycol, acol}) + } case cbTC16: for x := 0; x < d.width; x++ { rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1]) @@ -565,16 +681,20 @@ func DecodeConfig(r io.Reader) (image.Config, os.Error) { } var cm image.ColorModel switch d.cb { - case cbG8: + case cbG1, cbG2, cbG4, cbG8: cm = image.GrayColorModel + case cbGA8: + cm = image.NRGBAColorModel case cbTC8: cm = image.RGBAColorModel - case cbP8: + case cbP1, cbP2, cbP4, cbP8: cm = d.palette case cbTCA8: cm = image.NRGBAColorModel case cbG16: cm = image.Gray16ColorModel + case cbGA16: + cm = image.NRGBA64ColorModel case cbTC16: cm = image.RGBA64ColorModel case cbTCA16: diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go index fefceee3a5..8314a83387 100644 --- a/src/pkg/image/png/reader_test.go +++ b/src/pkg/image/png/reader_test.go @@ -13,23 +13,23 @@ import ( "testing" ) -// The go PNG library currently supports only a subset of the full PNG specification. -// In particular, bit depths other than 8 or 16 are not supported, nor are grayscale- -// alpha images. var filenames = []string{ - //"basn0g01", // bit depth is not 8 or 16 - //"basn0g02", // bit depth is not 8 or 16 - //"basn0g04", // bit depth is not 8 or 16 + "basn0g01", + "basn0g01-30", + "basn0g02", + "basn0g02-29", + "basn0g04", + "basn0g04-31", "basn0g08", "basn0g16", "basn2c08", "basn2c16", - //"basn3p01", // bit depth is not 8 or 16 - //"basn3p02", // bit depth is not 8 or 16 - //"basn3p04", // bit depth is not 8 or 16 + "basn3p01", + "basn3p02", + "basn3p04", "basn3p08", - //"basn4a08", // grayscale-alpha color model - //"basn4a16", // grayscale-alpha color model + "basn4a08", + "basn4a16", "basn6a08", "basn6a16", } @@ -58,7 +58,16 @@ func sng(w io.WriteCloser, filename string, png image.Image) { cpm, _ := cm.(image.PalettedColorModel) var paletted *image.Paletted if cpm != nil { - bitdepth = 8 + switch { + case len(cpm) <= 2: + bitdepth = 1 + case len(cpm) <= 4: + bitdepth = 2 + case len(cpm) <= 16: + bitdepth = 4 + default: + bitdepth = 8 + } paletted = png.(*image.Paletted) } @@ -131,8 +140,15 @@ func sng(w io.WriteCloser, filename string, png image.Image) { fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A) } case cpm != nil: + var b, c int for x := bounds.Min.X; x < bounds.Max.X; x++ { - fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y)) + b = b<