]> Cypherpunks repositories - gostls13.git/commitdiff
image/tiff: Implement PackBits decoding.
authorBenny Siegert <bsiegert@gmail.com>
Thu, 13 Oct 2011 02:31:26 +0000 (13:31 +1100)
committerNigel Tao <nigeltao@golang.org>
Thu, 13 Oct 2011 02:31:26 +0000 (13:31 +1100)
The decompression routine is in its own file because
G3 encoding (which is more complicated) will be put
there.

R=nigeltao
CC=golang-dev
https://golang.org/cl/5177047

src/pkg/image/tiff/Makefile
src/pkg/image/tiff/compress.go [new file with mode: 0644]
src/pkg/image/tiff/reader.go
src/pkg/image/tiff/reader_test.go
src/pkg/image/tiff/testdata/bw-deflate.tiff [new file with mode: 0644]
src/pkg/image/tiff/testdata/bw-packbits.tiff [new file with mode: 0644]
src/pkg/image/tiff/testdata/bw-uncompressed.tiff [new file with mode: 0644]

index 1a001afb9b6129ea67640ceb77e96911884241cf..67ef5c9ec626bb0e840cc0ada995df07994afda9 100644 (file)
@@ -7,6 +7,7 @@ include ../../../Make.inc
 TARG=image/tiff
 GOFILES=\
        buffer.go\
+       compress.go\
        consts.go\
        reader.go\
 
diff --git a/src/pkg/image/tiff/compress.go b/src/pkg/image/tiff/compress.go
new file mode 100644 (file)
index 0000000..e89aa6d
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tiff
+
+import (
+       "bufio"
+       "io"
+       "os"
+)
+
+type byteReader interface {
+       io.Reader
+       io.ByteReader
+}
+
+// unpackBits decodes the PackBits-compressed data in src and returns the
+// uncompressed data.
+//
+// The PackBits compression format is described in section 9 (p. 42)
+// of the TIFF spec.
+func unpackBits(r io.Reader) ([]byte, os.Error) {
+       buf := make([]byte, 128)
+       dst := make([]byte, 0, 1024)
+       br, ok := r.(byteReader)
+       if !ok {
+               br = bufio.NewReader(r)
+       }
+
+       for {
+               b, err := br.ReadByte()
+               if err != nil {
+                       if err == os.EOF {
+                               return dst, nil
+                       }
+                       return nil, err
+               }
+               code := int(int8(b))
+               switch {
+               case code >= 0:
+                       n, err := io.ReadFull(br, buf[:code+1])
+                       if err != nil {
+                               return nil, err
+                       }
+                       dst = append(dst, buf[:n]...)
+               case code == -128:
+                       // No-op.
+               default:
+                       if b, err = br.ReadByte(); err != nil {
+                               return nil, err
+                       }
+                       for j := 0; j < 1-code; j++ {
+                               buf[j] = b
+                       }
+                       dst = append(dst, buf[:1-code]...)
+               }
+       }
+       panic("unreachable")
+}
index 2db82bf21089b6fac9e94de5a26c4c6983fdcb78..c452f5d54ccf2f514c3c4ff2222ce8606692b729 100644 (file)
@@ -412,6 +412,8 @@ func Decode(r io.Reader) (img image.Image, err os.Error) {
                        }
                        d.buf, err = ioutil.ReadAll(r)
                        r.Close()
+               case cPackBits:
+                       d.buf, err = unpackBits(io.NewSectionReader(d.r, offset, n))
                default:
                        err = UnsupportedError("compression")
                }
index 1eb2bcd76e302a1ee055926ed4f09c16e850ac88..86b7dc3761939b7c7e13f66946ed016465cea446 100644 (file)
@@ -5,8 +5,10 @@
 package tiff
 
 import (
+       "image"
        "io/ioutil"
        "os"
+       "strings"
        "testing"
 )
 
@@ -30,6 +32,73 @@ func TestNoRPS(t *testing.T) {
        }
 }
 
+// TestUnpackBits tests the decoding of PackBits-encoded data.
+func TestUnpackBits(t *testing.T) {
+       var unpackBitsTests = []struct {
+               compressed   string
+               uncompressed string
+       }{{
+               // Example data from Wikipedia.
+               "\xfe\xaa\x02\x80\x00\x2a\xfd\xaa\x03\x80\x00\x2a\x22\xf7\xaa",
+               "\xaa\xaa\xaa\x80\x00\x2a\xaa\xaa\xaa\xaa\x80\x00\x2a\x22\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+       }}
+       for _, u := range unpackBitsTests {
+               buf, err := unpackBits(strings.NewReader(u.compressed))
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if string(buf) != u.uncompressed {
+                       t.Fatalf("unpackBits: want %x, got %x", u.uncompressed, buf)
+               }
+       }
+}
+
+// TestDecompress tests that decoding some TIFF images that use different
+// compression formats result in the same pixel data.
+func TestDecompress(t *testing.T) {
+       var decompressTests = []string{
+               "bw-uncompressed.tiff",
+               "bw-deflate.tiff",
+               "bw-packbits.tiff",
+       }
+       var img0 image.Image
+       for _, name := range decompressTests {
+               f, err := os.Open("testdata/" + name)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer f.Close()
+               if img0 == nil {
+                       img0, err = Decode(f)
+                       if err != nil {
+                               t.Fatalf("decoding %s: %v", name, err)
+                       }
+                       continue
+               }
+
+               img1, err := Decode(f)
+               if err != nil {
+                       t.Fatalf("decoding %s: %v", name, err)
+               }
+               b := img1.Bounds()
+               // Compare images.
+               if !b.Eq(img0.Bounds()) {
+                       t.Fatalf("wrong image size: want %s, got %s", img0.Bounds(), b)
+               }
+               for y := b.Min.Y; y < b.Max.Y; y++ {
+                       for x := b.Min.X; x < b.Max.X; x++ {
+                               c0 := img0.At(x, y)
+                               c1 := img1.At(x, y)
+                               r0, g0, b0, a0 := c0.RGBA()
+                               r1, g1, b1, a1 := c1.RGBA()
+                               if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
+                                       t.Fatalf("pixel at (%d, %d) has wrong color: want %v, got %v", x, y, c0, c1)
+                               }
+                       }
+               }
+       }
+}
+
 const filename = "testdata/video-001-uncompressed.tiff"
 
 // BenchmarkDecode benchmarks the decoding of an image.
diff --git a/src/pkg/image/tiff/testdata/bw-deflate.tiff b/src/pkg/image/tiff/testdata/bw-deflate.tiff
new file mode 100644 (file)
index 0000000..137a0c3
Binary files /dev/null and b/src/pkg/image/tiff/testdata/bw-deflate.tiff differ
diff --git a/src/pkg/image/tiff/testdata/bw-packbits.tiff b/src/pkg/image/tiff/testdata/bw-packbits.tiff
new file mode 100644 (file)
index 0000000..d59fa4a
Binary files /dev/null and b/src/pkg/image/tiff/testdata/bw-packbits.tiff differ
diff --git a/src/pkg/image/tiff/testdata/bw-uncompressed.tiff b/src/pkg/image/tiff/testdata/bw-uncompressed.tiff
new file mode 100644 (file)
index 0000000..8390f11
Binary files /dev/null and b/src/pkg/image/tiff/testdata/bw-uncompressed.tiff differ