summaryrefslogtreecommitdiff
path: root/libgo/go/image/png/reader.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/image/png/reader.go')
-rw-r--r--libgo/go/image/png/reader.go215
1 files changed, 171 insertions, 44 deletions
diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go
index a6bf86ede6..0a40ca161d 100644
--- a/libgo/go/image/png/reader.go
+++ b/libgo/go/image/png/reader.go
@@ -57,6 +57,29 @@ const (
nFilter = 5
)
+// Interlace type.
+const (
+ itNone = 0
+ itAdam7 = 1
+)
+
+// interlaceScan defines the placement and size of a pass for Adam7 interlacing.
+type interlaceScan struct {
+ xFactor, yFactor, xOffset, yOffset int
+}
+
+// interlacing defines Adam7 interlacing, with 7 passes of reduced images.
+// See http://www.w3.org/TR/PNG/#8Interlace
+var interlacing = []interlaceScan{
+ {8, 8, 0, 0},
+ {8, 8, 4, 0},
+ {4, 8, 0, 4},
+ {4, 4, 2, 0},
+ {2, 4, 0, 2},
+ {2, 2, 1, 0},
+ {1, 2, 0, 1},
+}
+
// Decoding stage.
// The PNG specification says that the IHDR, PLTE (if present), IDAT and IEND
// chunks must appear in that order. There may be multiple IDAT chunks, and
@@ -84,6 +107,7 @@ type decoder struct {
stage int
idatLength uint32
tmp [3 * 256]byte
+ interlace int
}
// A FormatError reports that the input is not a valid PNG.
@@ -113,9 +137,16 @@ func (d *decoder) parseIHDR(length uint32) error {
return err
}
d.crc.Write(d.tmp[:13])
- if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 {
- return UnsupportedError("compression, filter or interlace method")
+ if d.tmp[10] != 0 {
+ return UnsupportedError("compression method")
+ }
+ if d.tmp[11] != 0 {
+ return UnsupportedError("filter method")
+ }
+ if d.tmp[12] != itNone && d.tmp[12] != itAdam7 {
+ return FormatError("invalid interlace method")
}
+ d.interlace = int(d.tmp[12])
w := int32(binary.BigEndian.Uint32(d.tmp[0:4]))
h := int32(binary.BigEndian.Uint32(d.tmp[4:8]))
if w < 0 || h < 0 {
@@ -287,7 +318,42 @@ func (d *decoder) decode() (image.Image, error) {
return nil, err
}
defer r.Close()
- bitsPerPixel := 0
+ var img image.Image
+ if d.interlace == itNone {
+ img, err = d.readImagePass(r, 0, false)
+ } else if d.interlace == itAdam7 {
+ // Allocate a blank image of the full size.
+ img, err = d.readImagePass(nil, 0, true)
+ for pass := 0; pass < 7; pass++ {
+ imagePass, err := d.readImagePass(r, pass, false)
+ if err != nil {
+ return nil, err
+ }
+ d.mergePassInto(img, imagePass, pass)
+ }
+ }
+
+ // Check for EOF, to verify the zlib checksum.
+ n := 0
+ for i := 0; n == 0 && err == nil; i++ {
+ if i == 100 {
+ return nil, io.ErrNoProgress
+ }
+ n, err = r.Read(d.tmp[:1])
+ }
+ if err != nil && err != io.EOF {
+ return nil, FormatError(err.Error())
+ }
+ if n != 0 || d.idatLength != 0 {
+ return nil, FormatError("too much pixel data")
+ }
+
+ return img, nil
+}
+
+// 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
@@ -299,52 +365,63 @@ func (d *decoder) decode() (image.Image, error) {
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
+ }
switch d.cb {
case cbG1, cbG2, cbG4, cbG8:
bitsPerPixel = d.depth
- gray = image.NewGray(image.Rect(0, 0, d.width, d.height))
+ gray = image.NewGray(image.Rect(0, 0, width, height))
img = gray
case cbGA8:
bitsPerPixel = 16
- nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
+ nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
img = nrgba
case cbTC8:
bitsPerPixel = 24
- rgba = image.NewRGBA(image.Rect(0, 0, d.width, d.height))
+ 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, d.width, d.height), d.palette)
+ paletted = image.NewPaletted(image.Rect(0, 0, width, height), d.palette)
img = paletted
case cbTCA8:
bitsPerPixel = 32
- nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
+ nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
img = nrgba
case cbG16:
bitsPerPixel = 16
- gray16 = image.NewGray16(image.Rect(0, 0, d.width, d.height))
+ gray16 = image.NewGray16(image.Rect(0, 0, width, height))
img = gray16
case cbGA16:
bitsPerPixel = 32
- nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height))
+ nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
img = nrgba64
case cbTC16:
bitsPerPixel = 48
- rgba64 = image.NewRGBA64(image.Rect(0, 0, d.width, d.height))
+ rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height))
img = rgba64
case cbTCA16:
bitsPerPixel = 64
- nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height))
+ nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
img = nrgba64
}
+ if allocateOnly {
+ return img, nil
+ }
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)
+ 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 < d.height; y++ {
+ for y := 0; y < height; y++ {
// Read the decompressed bytes.
_, err := io.ReadFull(r, cr)
if err != nil {
@@ -381,25 +458,25 @@ func (d *decoder) decode() (image.Image, error) {
// Convert from bytes to colors.
switch d.cb {
case cbG1:
- for x := 0; x < d.width; x += 8 {
+ for x := 0; x < width; x += 8 {
b := cdat[x/8]
- for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
+ 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 < d.width; x += 4 {
+ for x := 0; x < width; x += 4 {
b := cdat[x/4]
- for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
+ 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 < d.width; x += 2 {
+ for x := 0; x < width; x += 2 {
b := cdat[x/2]
- for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
+ for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
b <<= 4
}
@@ -408,13 +485,13 @@ func (d *decoder) decode() (image.Image, error) {
copy(gray.Pix[pixOffset:], cdat)
pixOffset += gray.Stride
case cbGA8:
- for x := 0; x < d.width; x++ {
+ 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 < d.width; x++ {
+ for x := 0; x < width; x++ {
pix[i+0] = cdat[j+0]
pix[i+1] = cdat[j+1]
pix[i+2] = cdat[j+2]
@@ -424,9 +501,9 @@ func (d *decoder) decode() (image.Image, error) {
}
pixOffset += rgba.Stride
case cbP1:
- for x := 0; x < d.width; x += 8 {
+ for x := 0; x < width; x += 8 {
b := cdat[x/8]
- for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
+ 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]
@@ -436,9 +513,9 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbP2:
- for x := 0; x < d.width; x += 4 {
+ for x := 0; x < width; x += 4 {
b := cdat[x/4]
- for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
+ 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]
@@ -448,9 +525,9 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbP4:
- for x := 0; x < d.width; x += 2 {
+ for x := 0; x < width; x += 2 {
b := cdat[x/2]
- for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
+ 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]
@@ -461,7 +538,7 @@ func (d *decoder) decode() (image.Image, error) {
}
case cbP8:
if len(paletted.Palette) != 255 {
- for x := 0; x < d.width; x++ {
+ for x := 0; x < width; x++ {
if len(paletted.Palette) <= int(cdat[x]) {
paletted.Palette = paletted.Palette[:int(cdat[x])+1]
}
@@ -473,25 +550,25 @@ func (d *decoder) decode() (image.Image, error) {
copy(nrgba.Pix[pixOffset:], cdat)
pixOffset += nrgba.Stride
case cbG16:
- for x := 0; x < d.width; x++ {
+ 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 < d.width; x++ {
+ 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 < d.width; x++ {
+ 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 < d.width; x++ {
+ 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])
@@ -504,18 +581,68 @@ func (d *decoder) decode() (image.Image, error) {
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
}
+// mergePassInto merges a single pass into a full sized image.
+func (d *decoder) mergePassInto(dst image.Image, src image.Image, pass int) {
+ p := interlacing[pass]
+ var (
+ srcPix []uint8
+ dstPix []uint8
+ stride int
+ rect image.Rectangle
+ bytesPerPixel int
+ )
+ switch target := dst.(type) {
+ case *image.Alpha:
+ srcPix = src.(*image.Alpha).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 1
+ case *image.Alpha16:
+ srcPix = src.(*image.Alpha16).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 2
+ case *image.Gray:
+ srcPix = src.(*image.Gray).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 1
+ case *image.Gray16:
+ srcPix = src.(*image.Gray16).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 2
+ case *image.NRGBA:
+ srcPix = src.(*image.NRGBA).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 4
+ case *image.NRGBA64:
+ srcPix = src.(*image.NRGBA64).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 8
+ case *image.Paletted:
+ srcPix = src.(*image.Paletted).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 1
+ case *image.RGBA:
+ srcPix = src.(*image.RGBA).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 4
+ case *image.RGBA64:
+ srcPix = src.(*image.RGBA64).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 8
+ }
+ s, bounds := 0, src.Bounds()
+ for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
+ dBase := (y*p.yFactor+p.yOffset-rect.Min.Y)*stride + (p.xOffset-rect.Min.X)*bytesPerPixel
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
+ d := dBase + x*p.xFactor*bytesPerPixel
+ copy(dstPix[d:], srcPix[s:s+bytesPerPixel])
+ s += bytesPerPixel
+ }
+ }
+}
+
func (d *decoder) parseIDAT(length uint32) (err error) {
d.idatLength = length
d.img, err = d.decode()