From ff1093852faa2515751880bb98763858f613df97 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 16 May 2011 15:17:17 -0700 Subject: [PATCH] GIF: support decoding of interlaced images. R=nigeltao CC=golang-dev https://golang.org/cl/4535073 --- src/pkg/image/decode_test.go | 1 + src/pkg/image/gif/reader.go | 47 ++++++++++++++++-- .../image/testdata/video-001.interlaced.gif | Bin 0 -> 14142 bytes 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 src/pkg/image/testdata/video-001.interlaced.gif diff --git a/src/pkg/image/decode_test.go b/src/pkg/image/decode_test.go index e03b12deed..a0fba6fdba 100644 --- a/src/pkg/image/decode_test.go +++ b/src/pkg/image/decode_test.go @@ -29,6 +29,7 @@ var imageTests = []imageTest{ // GIF images are restricted to a 256-color palette and the conversion // to GIF loses significant image quality. {"testdata/video-001.gif", 64 << 8}, + {"testdata/video-001.interlaced.gif", 64 << 8}, // JPEG is a lossy format and hence needs a non-zero tolerance. {"testdata/video-001.jpeg", 8 << 8}, {"testdata/video-001.png", 0}, diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go index e083b87100..e27b74b64d 100644 --- a/src/pkg/image/gif/reader.go +++ b/src/pkg/image/gif/reader.go @@ -191,6 +191,10 @@ Loop: if c != 0 { return os.ErrorString("gif: extra data after image") } + + // Undo the interlacing if necessary. + d.uninterlace(m) + d.image = append(d.image, m) d.delay = append(d.delay, d.delayTime) d.delayTime = 0 // TODO: is this correct, or should we hold on to the value? @@ -321,14 +325,14 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, os.Error) { if _, err := io.ReadFull(d.r, d.tmp[0:9]); err != nil { return nil, fmt.Errorf("gif: can't read image descriptor: %s", err) } + // TODO: This code (throughout) ignores the top and left values, + // and assumes (in interlacing, for example) that the images' + // widths and heights are all the same. _ = int(d.tmp[0]) + int(d.tmp[1])<<8 // TODO: honor left value _ = int(d.tmp[2]) + int(d.tmp[3])<<8 // TODO: honor top value width := int(d.tmp[4]) + int(d.tmp[5])<<8 height := int(d.tmp[6]) + int(d.tmp[7])<<8 d.imageFields = d.tmp[8] - if d.imageFields&ifInterlace != 0 { - return nil, os.ErrorString("gif: can't handle interlaced images") - } return image.NewPaletted(width, height, nil), nil } @@ -340,9 +344,42 @@ func (d *decoder) readBlock() (int, os.Error) { return io.ReadFull(d.r, d.tmp[0:n]) } +// interlaceScan defines the ordering for a pass of the interlace algorithm. +type interlaceScan struct { + skip, start int +} + +// interlacing represents the set of scans in an interlaced GIF image. +var interlacing = []interlaceScan{ + {8, 0}, // Group 1 : Every 8th. row, starting with row 0. + {8, 4}, // Group 2 : Every 8th. row, starting with row 4. + {4, 2}, // Group 3 : Every 4th. row, starting with row 2. + {2, 1}, // Group 4 : Every 2nd. row, starting with row 1. +} + +func (d *decoder) uninterlace(m *image.Paletted) { + if d.imageFields&ifInterlace == 0 { + return + } + var nPix []uint8 + dx := d.width + dy := d.height + nPix = make([]uint8, dx*dy) + offset := 0 // steps through the input by sequentical scan lines. + for _, pass := range interlacing { + nOffset := pass.start * dx // steps through the output as defined by pass. + for y := pass.start; y < dy; y += pass.skip { + copy(nPix[nOffset:nOffset+dx], m.Pix[offset:offset+dx]) + offset += dx + nOffset += dx * pass.skip + } + } + m.Pix = nPix +} + // Decode reads a GIF image from r and returns the first embedded // image as an image.Image. -// Limitation: The file must be 8 bits per pixel and have no interlacing. +// Limitation: The file must be 8 bits per pixel. func Decode(r io.Reader) (image.Image, os.Error) { var d decoder if err := d.decode(r, false); err != nil { @@ -360,7 +397,7 @@ type GIF struct { // DecodeAll reads a GIF image from r and returns the sequential frames // and timing information. -// Limitation: The file must be 8 bits per pixel and have no interlacing. +// Limitation: The file must be 8 bits per pixel. func DecodeAll(r io.Reader) (*GIF, os.Error) { var d decoder if err := d.decode(r, false); err != nil { diff --git a/src/pkg/image/testdata/video-001.interlaced.gif b/src/pkg/image/testdata/video-001.interlaced.gif new file mode 100644 index 0000000000000000000000000000000000000000..590594ea9a76c396ebe5ae3fde1a385eb5815c6d GIT binary patch literal 14142 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 literal 0 HcmV?d00001 -- 2.50.0