// readImagePass reads a single image pass, sized according to the pass number. func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image.Image, error) { var bitsPerPixel int = 0 pixOffset := 0 var ( gray *image.Gray rgba *image.RGBA paletted *image.Paletted nrgba *image.NRGBA gray16 *image.Gray16 rgba64 *image.RGBA64 nrgba64 *image.NRGBA64 img image.Image ) width, height := d.width, d.height if d.interlace == itAdam7 && !allocateOnly { p := interlacing[pass] // Add the multiplication factor and subtract one, effectively rounding up. width = (width - p.xOffset + p.xFactor - 1) / p.xFactor height = (height - p.yOffset + p.yFactor - 1) / p.yFactor // A PNG image can't have zero width or height, but for an interlaced // image, an individual pass might have zero width or height. If so, we // shouldn't even read a per-row filter type byte, so return early. if width == 0 || height == 0 { return nil, nil } } switch d.cb { case cbG1, cbG2, cbG4, cbG8: bitsPerPixel = d.depth gray = image.NewGray(image.Rect(0, 0, width, height)) img = gray case cbGA8: bitsPerPixel = 16 nrgba = image.NewNRGBA(image.Rect(0, 0, width, height)) img = nrgba case cbTC8: bitsPerPixel = 24 rgba = image.NewRGBA(image.Rect(0, 0, width, height)) img = rgba case cbP1, cbP2, cbP4, cbP8: bitsPerPixel = d.depth paletted = image.NewPaletted(image.Rect(0, 0, width, height), d.palette) img = paletted case cbTCA8: bitsPerPixel = 32 nrgba = image.NewNRGBA(image.Rect(0, 0, width, height)) img = nrgba case cbG16: bitsPerPixel = 16 gray16 = image.NewGray16(image.Rect(0, 0, width, height)) img = gray16 case cbGA16: bitsPerPixel = 32 nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height)) img = nrgba64 case cbTC16: bitsPerPixel = 48 rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height)) img = rgba64 case cbTCA16: bitsPerPixel = 64 nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height)) img = nrgba64 } if allocateOnly { return img, nil } bytesPerPixel := (bitsPerPixel + 7) / 8 // The +1 is for the per-row filter type, which is at cr[0]. rowSize := 1 + (bitsPerPixel*width+7)/8 // cr and pr are the bytes for the current and previous row. cr := make([]uint8, rowSize) pr := make([]uint8, rowSize) for y := 0; y < height; y++ { // Read the decompressed bytes. _, err := io.ReadFull(r, cr) if err != nil { if err == io.EOF || err == io.ErrUnexpectedEOF { return nil, FormatError("not enough pixel data") } return nil, err } // Apply the filter. cdat := cr[1:] pdat := pr[1:] switch cr[0] { case ftNone: // No-op. case ftSub: for i := bytesPerPixel; i < len(cdat); i++ { cdat[i] += cdat[i-bytesPerPixel] } case ftUp: for i, p := range pdat { cdat[i] += p } case ftAverage: // The first column has no column to the left of it, so it is a // special case. We know that the first column exists because we // check above that width != 0, and so len(cdat) != 0. for i := 0; i < bytesPerPixel; i++ { cdat[i] += pdat[i] / 2 } for i := bytesPerPixel; i < len(cdat); i++ { cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2) } case ftPaeth: filterPaeth(cdat, pdat, bytesPerPixel) default: return nil, FormatError("bad filter type") } // Convert from bytes to colors. switch d.cb { case cbG1: for x := 0; x < width; x += 8 { b := cdat[x/8] for x2 := 0; x2 < 8 && x+x2 < width; x2++ { gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff}) b <<= 1 } } case cbG2: for x := 0; x < width; x += 4 { b := cdat[x/4] for x2 := 0; x2 < 4 && x+x2 < width; x2++ { gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55}) b <<= 2 } } case cbG4: for x := 0; x < width; x += 2 { b := cdat[x/2] for x2 := 0; x2 < 2 && x+x2 < width; x2++ { gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11}) b <<= 4 } } case cbG8: copy(gray.Pix[pixOffset:], cdat) pixOffset += gray.Stride case cbGA8: for x := 0; x < width; x++ { ycol := cdat[2*x+0] nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]}) } case cbTC8: pix, i, j := rgba.Pix, pixOffset, 0 for x := 0; x < width; x++ { pix[i+0] = cdat[j+0] pix[i+1] = cdat[j+1] pix[i+2] = cdat[j+2] pix[i+3] = 0xff i += 4 j += 3 } pixOffset += rgba.Stride case cbP1: for x := 0; x < width; x += 8 { b := cdat[x/8] for x2 := 0; x2 < 8 && x+x2 < width; x2++ { idx := b >> 7 if len(paletted.Palette) <= int(idx) { paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 1 } } case cbP2: for x := 0; x < width; x += 4 { b := cdat[x/4] for x2 := 0; x2 < 4 && x+x2 < width; x2++ { idx := b >> 6 if len(paletted.Palette) <= int(idx) { paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 2 } } case cbP4: for x := 0; x < width; x += 2 { b := cdat[x/2] for x2 := 0; x2 < 2 && x+x2 < width; x2++ { idx := b >> 4 if len(paletted.Palette) <= int(idx) { paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 4 } } case cbP8: if len(paletted.Palette) != 255 { for x := 0; x < width; x++ { if len(paletted.Palette) <= int(cdat[x]) { paletted.Palette = paletted.Palette[:int(cdat[x])+1] } } } copy(paletted.Pix[pixOffset:], cdat) pixOffset += paletted.Stride case cbTCA8: copy(nrgba.Pix[pixOffset:], cdat) pixOffset += nrgba.Stride case cbG16: for x := 0; x < width; x++ { ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) gray16.SetGray16(x, y, color.Gray16{ycol}) } case cbGA16: for x := 0; x < 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.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol}) } case cbTC16: for x := 0; x < width; x++ { rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1]) gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3]) bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5]) rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff}) } case cbTCA16: for x := 0; x < width; x++ { rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1]) gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3]) bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5]) acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7]) nrgba64.SetNRGBA64(x, y, color.NRGBA64{rcol, gcol, bcol, acol}) } } // The current row for y is the previous row for y+1. pr, cr = cr, pr } return img, nil }
// decode decodes the IDAT data into an image. func (d *decoder) decode() (image.Image, error) { r, err := zlib.NewReader(d) if err != nil { return nil, err } defer r.Close() bitsPerPixel := 0 pixOffset := 0 var ( gray *image.Gray rgba *image.RGBA paletted *image.Paletted nrgba *image.NRGBA gray16 *image.Gray16 rgba64 *image.RGBA64 nrgba64 *image.NRGBA64 img image.Image ) switch d.cb { case cbG1, cbG2, cbG4, cbG8: bitsPerPixel = d.depth gray = image.NewGray(image.Rect(0, 0, d.width, d.height)) img = gray case cbGA8: bitsPerPixel = 16 nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) img = nrgba case cbTC8: bitsPerPixel = 24 rgba = image.NewRGBA(image.Rect(0, 0, d.width, d.height)) img = rgba case cbP1, cbP2, cbP4, cbP8: bitsPerPixel = d.depth paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.palette) img = paletted case cbTCA8: bitsPerPixel = 32 nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) img = nrgba case cbG16: bitsPerPixel = 16 gray16 = image.NewGray16(image.Rect(0, 0, d.width, d.height)) img = gray16 case cbGA16: bitsPerPixel = 32 nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height)) img = nrgba64 case cbTC16: bitsPerPixel = 48 rgba64 = image.NewRGBA64(image.Rect(0, 0, d.width, d.height)) img = rgba64 case cbTCA16: bitsPerPixel = 64 nrgba64 = image.NewNRGBA64(image.Rect(0, 0, 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+(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. _, err := io.ReadFull(r, cr) if err != nil { return nil, err } // Apply the filter. cdat := cr[1:] pdat := pr[1:] switch cr[0] { case ftNone: // No-op. case ftSub: for i := bytesPerPixel; i < len(cdat); i++ { cdat[i] += cdat[i-bytesPerPixel] } case ftUp: for i, p := range pdat { cdat[i] += p } case ftAverage: for i := 0; i < bytesPerPixel; i++ { cdat[i] += pdat[i] / 2 } for i := bytesPerPixel; i < len(cdat); i++ { cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2) } case ftPaeth: filterPaeth(cdat, pdat, bytesPerPixel) default: return nil, FormatError("bad filter type") } // 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.SetGray(x+x2, y, color.Gray{(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.SetGray(x+x2, y, color.Gray{(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.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11}) b <<= 4 } } case cbG8: copy(gray.Pix[pixOffset:], cdat) pixOffset += gray.Stride case cbGA8: for x := 0; x < d.width; x++ { ycol := cdat[2*x+0] nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]}) } case cbTC8: pix, i, j := rgba.Pix, pixOffset, 0 for x := 0; x < d.width; x++ { pix[i+0] = cdat[j+0] pix[i+1] = cdat[j+1] pix[i+2] = cdat[j+2] pix[i+3] = 0xff i += 4 j += 3 } pixOffset += rgba.Stride 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 len(paletted.Palette) <= int(idx) { paletted.Palette = paletted.Palette[:int(idx)+1] } 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 len(paletted.Palette) <= int(idx) { paletted.Palette = paletted.Palette[:int(idx)+1] } 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 len(paletted.Palette) <= int(idx) { paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 4 } } case cbP8: if len(paletted.Palette) != 255 { for x := 0; x < d.width; x++ { if len(paletted.Palette) <= int(cdat[x]) { paletted.Palette = paletted.Palette[:int(cdat[x])+1] } } } copy(paletted.Pix[pixOffset:], cdat) pixOffset += paletted.Stride case cbTCA8: copy(nrgba.Pix[pixOffset:], cdat) pixOffset += nrgba.Stride case cbG16: for x := 0; x < d.width; x++ { ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) gray16.SetGray16(x, y, color.Gray16{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.SetNRGBA64(x, y, color.NRGBA64{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]) gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3]) bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5]) rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff}) } case cbTCA16: for x := 0; x < d.width; x++ { rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1]) gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3]) bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5]) acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7]) nrgba64.SetNRGBA64(x, y, color.NRGBA64{rcol, gcol, bcol, acol}) } } // The current row for y is the previous row for y+1. pr, cr = cr, pr } // Check for EOF, to verify the zlib checksum. n, err := r.Read(pr[:1]) if err != io.EOF { return nil, FormatError(err.Error()) } if n != 0 || d.idatLength != 0 { return nil, FormatError("too much pixel data") } return img, nil }
func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { r, err := zlib.NewReader(idat) if err != nil { return nil, err } defer r.Close() bitsPerPixel := 0 maxPalette := uint8(0) var ( gray *image.Gray rgba *image.RGBA paletted *image.Paletted nrgba *image.NRGBA gray16 *image.Gray16 rgba64 *image.RGBA64 nrgba64 *image.NRGBA64 img image.Image ) switch d.cb { 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: bitsPerPixel = 24 rgba = image.NewRGBA(d.width, d.height) img = rgba 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: bitsPerPixel = 32 nrgba = image.NewNRGBA(d.width, d.height) img = nrgba case cbG16: 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: bitsPerPixel = 48 rgba64 = image.NewRGBA64(d.width, d.height) img = rgba64 case cbTCA16: 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+(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. _, err := io.ReadFull(r, cr) if err != nil { return nil, err } // Apply the filter. cdat := cr[1:] pdat := pr[1:] switch cr[0] { case ftNone: // No-op. case ftSub: 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 < bytesPerPixel; i++ { cdat[i] += 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 < bytesPerPixel; i++ { cdat[i] += paeth(0, pdat[i], 0) } 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") } // 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.SetGray(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.SetGray(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.SetGray(x+x2, y, image.GrayColor{(b >> 4) * 0x11}) b <<= 4 } } case cbG8: for x := 0; x < d.width; x++ { gray.SetGray(x, y, image.GrayColor{cdat[x]}) } case cbGA8: for x := 0; x < d.width; x++ { ycol := cdat[2*x+0] nrgba.SetNRGBA(x, y, image.NRGBAColor{ycol, ycol, ycol, cdat[2*x+1]}) } case cbTC8: for x := 0; x < d.width; x++ { rgba.SetRGBA(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 { return nil, FormatError("palette index out of range") } paletted.SetColorIndex(x, y, cdat[x]) } case cbTCA8: for x := 0; x < d.width; x++ { nrgba.SetNRGBA(x, y, image.NRGBAColor{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]}) } case cbG16: for x := 0; x < d.width; x++ { ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) gray16.SetGray16(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.SetNRGBA64(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]) gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3]) bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5]) rgba64.SetRGBA64(x, y, image.RGBA64Color{rcol, gcol, bcol, 0xffff}) } case cbTCA16: for x := 0; x < d.width; x++ { rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1]) gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3]) bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5]) acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7]) nrgba64.SetNRGBA64(x, y, image.NRGBA64Color{rcol, gcol, bcol, acol}) } } // The current row for y is the previous row for y+1. pr, cr = cr, pr } return img, nil }