Revision: 51f26e36ba98
Branch: default
Author: Nigel Tao <
nige...@golang.org>
Date: Sun Oct 14 17:21:20 2012
Log: image/jpeg: decode progressive JPEGs.
To be clear, this supports decoding the bytes on the wire into an
in-memory image. There is no API change: jpeg.Decode will still not
return until the entire image is decoded.
The code is obviously more complicated, and costs around 10% in
performance on baseline JPEGs. The processSOS code could be cleaned up a
bit, and maybe some of that loss can be reclaimed, but I'll leave that
for follow-up CLs, to keep the diff for this one as small as possible.
Before:
BenchmarkDecode 1000 2855637 ns/op 21.64 MB/s
After:
BenchmarkDecodeBaseline 500 3178960 ns/op 19.44 MB/s
BenchmarkDecodeProgressive 500 4082640 ns/op 15.14 MB/s
Fixes issue 3976.
The test data was generated by:
# Create intermediate files; cjpeg on Ubuntu 10.04 can't read PNG.
convert video-001.png video-001.bmp
convert video-005.gray.png video-005.gray.pgm
# Create new test files.
cjpeg -quality 100 -sample 1x1,1x1,1x1 -progressive video-001.bmp >
video-001.progressive.jpeg
cjpeg -quality 50 -sample 2x2,1x1,1x1 video-001.bmp > video-001.q50.420.jpeg
cjpeg -quality 50 -sample 2x1,1x1,1x1 video-001.bmp > video-001.q50.422.jpeg
cjpeg -quality 50 -sample 1x1,1x1,1x1 video-001.bmp > video-001.q50.444.jpeg
cjpeg -quality 50 -sample 2x2,1x1,1x1 -progressive video-001.bmp >
video-001.q50.420.progressive.jpeg
cjpeg -quality 50 -sample 2x1,1x1,1x1 -progressive video-001.bmp >
video-001.q50.422.progressive.jpeg
cjpeg -quality 50 -sample 1x1,1x1,1x1 -progressive video-001.bmp >
video-001.q50.444.progressive.jpeg
cjpeg -quality 50 video-005.gray.pgm > video-005.gray.q50.jpeg
cjpeg -quality 50 -progressive video-005.gray.pgm >
video-005.gray.q50.progressive.jpeg
# Delete intermediate files.
rm video-001.bmp video-005.gray.pgm
R=r
CC=golang-dev
http://codereview.appspot.com/6684046
http://code.google.com/p/go/source/detail?r=51f26e36ba98
Added:
/src/pkg/image/jpeg/reader_test.go
/src/pkg/image/jpeg/scan.go
/src/pkg/image/testdata/video-001.progressive.jpeg
/src/pkg/image/testdata/video-001.q50.420.jpeg
/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg
/src/pkg/image/testdata/video-001.q50.422.jpeg
/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg
/src/pkg/image/testdata/video-001.q50.444.jpeg
/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg
/src/pkg/image/testdata/video-005.gray.q50.jpeg
/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg
Modified:
/src/pkg/image/decode_test.go
/src/pkg/image/jpeg/huffman.go
/src/pkg/image/jpeg/reader.go
/src/pkg/image/jpeg/writer_test.go
=======================================
--- /dev/null
+++ /src/pkg/image/jpeg/reader_test.go Sun Oct 14 17:21:20 2012
@@ -0,0 +1,155 @@
+// Copyright 2012 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 jpeg
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+// TestDecodeProgressive tests that decoding the baseline and progressive
+// versions of the same image result in exactly the same pixel data, in
YCbCr
+// space for color images, and Y space for grayscale images.
+func TestDecodeProgressive(t *testing.T) {
+ testCases := []string{
+ "../testdata/video-001",
+ "../testdata/video-001.q50.420",
+ "../testdata/video-001.q50.422",
+ "../testdata/video-001.q50.444",
+ "../testdata/video-005.gray.q50",
+ }
+ for _, tc := range testCases {
+ m0, err := decodeFile(tc + ".jpeg")
+ if err != nil {
+ t.Errorf("%s: %v", tc+".jpeg", err)
+ continue
+ }
+ m1, err := decodeFile(tc + ".progressive.jpeg")
+ if err != nil {
+ t.Errorf("%s: %v", tc+".progressive.jpeg", err)
+ continue
+ }
+ if m0.Bounds() != m1.Bounds() {
+ t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
+ continue
+ }
+ switch m0 := m0.(type) {
+ case *image.YCbCr:
+ m1 := m1.(*image.YCbCr)
+ if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride);
err != nil {
+ t.Errorf("%s (Y): %v", tc, err)
+ continue
+ }
+ if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride);
err != nil {
+ t.Errorf("%s (Cb): %v", tc, err)
+ continue
+ }
+ if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride);
err != nil {
+ t.Errorf("%s (Cr): %v", tc, err)
+ continue
+ }
+ case *image.Gray:
+ m1 := m1.(*image.Gray)
+ if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride);
err != nil {
+ t.Errorf("%s: %v", tc, err)
+ continue
+ }
+ default:
+ t.Errorf("%s: unexpected image type %T", tc, m0)
+ continue
+ }
+ }
+}
+
+func decodeFile(filename string) (image.Image, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return Decode(f)
+
+}
+
+// check checks that the two pix data are equal, within the given bounds.
+func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1
int) error {
+ if len(pix0) != len(pix1) {
+ return fmt.Errorf("len(pix) %d and %d differ", len(pix0), len(pix1))
+ }
+ if stride0 != stride1 {
+ return fmt.Errorf("strides %d and %d differ", stride0, stride1)
+ }
+ if stride0%8 != 0 {
+ return fmt.Errorf("stride %d is not a multiple of 8", stride0)
+ }
+ // Compare the two pix data, one 8x8 block at a time.
+ for y := 0; y < len(pix0)/stride0; y += 8 {
+ for x := 0; x < stride0; x += 8 {
+ if x >= bounds.Max.X || y >= bounds.Max.Y {
+ // We don't care if the two pix data differ if the 8x8 block is
+ // entirely outside of the image's bounds. For example, this can
+ // occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline
+ // decoding works on the one 16x16 MCU as a whole; progressive
+ // decoding's first pass works on that 16x16 MCU as a whole but
+ // refinement passes only process one 8x8 block within the MCU.
+ continue
+ }
+
+ for j := 0; j < 8; j++ {
+ for i := 0; i < 8; i++ {
+ index := (y+j)*stride0 + (x + i)
+ if pix0[index] != pix1[index] {
+ return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
+ pixString(pix0, stride0, x, y),
+ pixString(pix1, stride1, x, y),
+ )
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func pixString(pix []byte, stride, x, y int) string {
+ s := bytes.NewBuffer(nil)
+ for j := 0; j < 8; j++ {
+ fmt.Fprintf(s, "\t")
+ for i := 0; i < 8; i++ {
+ fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)])
+ }
+ fmt.Fprintf(s, "\n")
+ }
+ return s.String()
+}
+
+func benchmarkDecode(b *testing.B, filename string) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ b.Fatal(err)
+ }
+ cfg, err := DecodeConfig(bytes.NewReader(data))
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(cfg.Width * cfg.Height * 4))
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Decode(bytes.NewReader(data))
+ }
+}
+
+func BenchmarkDecodeBaseline(b *testing.B) {
+ benchmarkDecode(b, "../testdata/video-001.jpeg")
+}
+
+func BenchmarkDecodeProgressive(b *testing.B) {
+ benchmarkDecode(b, "../testdata/video-001.progressive.jpeg")
+}
=======================================
--- /dev/null
+++ /src/pkg/image/jpeg/scan.go Sun Oct 14 17:21:20 2012
@@ -0,0 +1,111 @@
+// Copyright 2012 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 jpeg
+
+// refine decodes a successive approximation refinement block, as
specified in
+// section G.1.2.
+func (d *decoder) refine(b *block, h *huffman, zigStart, zigEnd, delta
int) error {
+ // Refining a DC component is trivial.
+ if zigStart == 0 {
+ if zigEnd != 0 {
+ panic("unreachable")
+ }
+ bit, err := d.decodeBit()
+ if err != nil {
+ return err
+ }
+ if bit {
+ b[0] |= delta
+ }
+ return nil
+ }
+
+ // Refining AC components is more complicated; see sections G.1.2.2 and
G.1.2.3.
+ zig := zigStart
+ if d.eobRun == 0 {
+ loop:
+ for ; zig <= zigEnd; zig++ {
+ z := 0
+ value, err := d.decodeHuffman(h)
+ if err != nil {
+ return err
+ }
+ val0 := value >> 4
+ val1 := value & 0x0f
+
+ switch val1 {
+ case 0:
+ if val0 != 0x0f {
+ d.eobRun = uint16(1 << val0)
+ if val0 != 0 {
+ bits, err := d.decodeBits(int(val0))
+ if err != nil {
+ return err
+ }
+ d.eobRun |= uint16(bits)
+ }
+ break loop
+ }
+ case 1:
+ z = delta
+ bit, err := d.decodeBit()
+ if err != nil {
+ return err
+ }
+ if !bit {
+ z = -z
+ }
+ default:
+ return FormatError("unexpected Huffman code")
+ }
+
+ zig, err = d.refineNonZeroes(b, zig, zigEnd, int(val0), delta)
+ if err != nil {
+ return err
+ }
+ if zig > zigEnd {
+ return FormatError("too many coefficients")
+ }
+ if z != 0 {
+ b[unzig[zig]] = z
+ }
+ }
+ }
+ if d.eobRun > 0 {
+ d.eobRun--
+ if _, err := d.refineNonZeroes(b, zig, zigEnd, -1, delta); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// refineNonZeroes refines non-zero entries of b in zig-zag order. If nz
>= 0,
+// the first nz zero entries are skipped over.
+func (d *decoder) refineNonZeroes(b *block, zig, zigEnd, nz, delta int)
(int, error) {
+ for ; zig <= zigEnd; zig++ {
+ u := unzig[zig]
+ if b[u] == 0 {
+ if nz == 0 {
+ break
+ }
+ nz--
+ continue
+ }
+ bit, err := d.decodeBit()
+ if err != nil {
+ return 0, err
+ }
+ if !bit {
+ continue
+ }
+ if b[u] >= 0 {
+ b[u] += delta
+ } else {
+ b[u] -= delta
+ }
+ }
+ return zig, nil
+}
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.progressive.jpeg Sun Oct 14 17:21:20
2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.q50.420.jpeg Sun Oct 14 17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.q50.420.progressive.jpeg Sun Oct 14
17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.q50.422.jpeg Sun Oct 14 17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.q50.422.progressive.jpeg Sun Oct 14
17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.q50.444.jpeg Sun Oct 14 17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-001.q50.444.progressive.jpeg Sun Oct 14
17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-005.gray.q50.jpeg Sun Oct 14 17:21:20 2012
Binary file, no diff available.
=======================================
--- /dev/null
+++ /src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg Sun Oct 14
17:21:20 2012
Binary file, no diff available.
=======================================
--- /src/pkg/image/decode_test.go Mon Jan 30 19:01:53 2012
+++ /src/pkg/image/decode_test.go Sun Oct 14 17:21:20 2012
@@ -31,6 +31,7 @@
{"testdata/video-001.png", "testdata/video-001.5bpp.gif", 128 << 8},
// JPEG is a lossy format and hence needs a non-zero tolerance.
{"testdata/video-001.png", "testdata/video-001.jpeg", 8 << 8},
+ {"testdata/video-001.png", "testdata/video-001.progressive.jpeg", 8 << 8},
// Grayscale images.
{"testdata/video-005.gray.png", "testdata/video-005.gray.jpeg", 8 << 8},
{"testdata/video-005.gray.png", "testdata/video-005.gray.png", 0},
=======================================
--- /src/pkg/image/jpeg/huffman.go Tue Nov 1 19:04:37 2011
+++ /src/pkg/image/jpeg/huffman.go Sun Oct 14 17:21:20 2012
@@ -15,9 +15,9 @@
// Bit stream for the Huffman decoder.
// The n least significant bits of a form the unread bits, to be read in
MSB to LSB order.
type bits struct {
- a int // accumulator.
- n int // the number of unread bits in a.
- m int // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
+ a uint32 // accumulator.
+ m uint32 // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
+ n int // the number of unread bits in a.
}
// Huffman table decoder, specified in section C.
@@ -39,7 +39,7 @@
if err != nil {
return err
}
- d.b.a = d.b.a<<8 | int(c)
+ d.b.a = d.b.a<<8 | uint32(c)
d.b.n += 8
if d.b.m == 0 {
d.b.m = 1 << 7
@@ -69,7 +69,7 @@
d.b.n -= int(t)
d.b.m >>= t
s := 1 << t
- x := (d.b.a >> uint8(d.b.n)) & (s - 1)
+ x := int(d.b.a>>uint8(d.b.n)) & (s - 1)
if x < s>>1 {
x += ((-1) << t) + 1
}
@@ -92,8 +92,7 @@
return FormatError("bad Tc value")
}
th := d.tmp[0] & 0x0f
- const isBaseline = true // Progressive mode is not yet supported.
- if th > maxTh || isBaseline && th > 1 {
+ if th > maxTh || !d.progressive && th > 1 {
return FormatError("bad Th value")
}
h := &d.huff[tc][th]
@@ -185,3 +184,28 @@
}
return 0, FormatError("bad Huffman code")
}
+
+func (d *decoder) decodeBit() (bool, error) {
+ if d.b.n == 0 {
+ err := d.ensureNBits(1)
+ if err != nil {
+ return false, err
+ }
+ }
+ ret := d.b.a&d.b.m != 0
+ d.b.n--
+ d.b.m >>= 1
+ return ret, nil
+}
+
+func (d *decoder) decodeBits(n int) (uint32, error) {
+ err := d.ensureNBits(n)
+ if err != nil {
+ return 0, err
+ }
+ ret := d.b.a >> uint(d.b.n-n)
+ ret &= (1 << uint(n)) - 1
+ d.b.n -= n
+ d.b.m >>= uint(n)
+ return ret, nil
+}
=======================================
--- /src/pkg/image/jpeg/reader.go Sun Oct 7 01:32:28 2012
+++ /src/pkg/image/jpeg/reader.go Sun Oct 14 17:21:20 2012
@@ -98,7 +98,10 @@
img3 *image.YCbCr
ri int // Restart Interval.
nComp int
+ progressive bool
+ eobRun uint16 // End-of-Band run, specified in section G.1.2.2.
comp [nColorComponent]component
+ progCoeffs [nColorComponent][]block // Saved state between
progressive-mode scans.
huff [maxTc + 1][maxTh + 1]huffman
quant [maxTq + 1]block // Quantization tables, in zig-zag order.
tmp [1024]byte
@@ -216,118 +219,253 @@
m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio)
d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr)
}
+
+// TODO(nigeltao): move processSOS to scan.go.
// Specified in section B.2.3.
func (d *decoder) processSOS(n int) error {
if d.nComp == 0 {
return FormatError("missing SOF marker")
}
- if n != 4+2*d.nComp {
- return UnsupportedError("SOS has wrong length")
+ if n < 6 || 4+2*d.nComp < n || n%2 != 0 {
+ return FormatError("SOS has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[0:4+2*d.nComp])
+ _, err := io.ReadFull(d.r, d.tmp[:n])
if err != nil {
return err
}
- if int(d.tmp[0]) != d.nComp {
- return UnsupportedError("SOS has wrong number of image components")
+ nComp := int(d.tmp[0])
+ if n != 4+2*nComp {
+ return FormatError("SOS length inconsistent with number of components")
}
var scan [nColorComponent]struct {
- td uint8 // DC table selector.
- ta uint8 // AC table selector.
+ compIndex uint8
+ td uint8 // DC table selector.
+ ta uint8 // AC table selector.
}
- for i := 0; i < d.nComp; i++ {
+ for i := 0; i < nComp; i++ {
cs := d.tmp[1+2*i] // Component selector.
- if cs != d.comp[i].c {
- return UnsupportedError("scan components out of order")
+ compIndex := -1
+ for j, comp := range d.comp {
+ if cs == comp.c {
+ compIndex = j
+ }
}
+ if compIndex < 0 {
+ return FormatError("unknown component selector")
+ }
+ scan[i].compIndex = uint8(compIndex)
scan[i].td = d.tmp[2+2*i] >> 4
scan[i].ta = d.tmp[2+2*i] & 0x0f
}
+
+ // zigStart and zigEnd are the spectral selection bounds.
+ // ah and al are the successive approximation high and low values.
+ // The spec calls these values Ss, Se, Ah and Al.
+ //
+ // For progressive JPEGs, these are the two more-or-less independent
+ // aspects of progression. Spectral selection progression is when not
+ // all of a block's 64 DCT coefficients are transmitted in one pass.
+ // For example, three passes could transmit coefficient 0 (the DC
+ // component), coefficients 1-5, and coefficients 6-63, in zig-zag
+ // order. Successive approximation is when not all of the bits of a
+ // band of coefficients are transmitted in one pass. For example,
+ // three passes could transmit the 6 most significant bits, followed
+ // by the second-least significant bit, followed by the least
+ // significant bit.
+ //
+ // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
+ zigStart, zigEnd, ah, al := 0, blockSize-1, uint(0), uint(0)
+ if d.progressive {
+ zigStart = int(d.tmp[1+2*nComp])
+ zigEnd = int(d.tmp[2+2*nComp])
+ ah = uint(d.tmp[3+2*nComp] >> 4)
+ al = uint(d.tmp[3+2*nComp] & 0x0f)
+ if (zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || blockSize <=
zigEnd {
+ return FormatError("bad spectral selection bounds")
+ }
+ if zigStart != 0 && nComp != 1 {
+ return FormatError("progressive AC coefficients for more than one
component")
+ }
+ if ah != 0 && ah != al+1 {
+ return FormatError("bad successive approximation values")
+ }
+ }
+
// mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y
components.
mxx := (d.width + 8*h0 - 1) / (8 * h0)
myy := (d.height + 8*v0 - 1) / (8 * v0)
if d.img1 == nil && d.img3 == nil {
d.makeImg(h0, v0, mxx, myy)
+ if d.progressive {
+ for i := 0; i < nComp; i++ {
+ compIndex := scan[i].compIndex
+ d.progCoeffs[compIndex] = make([]block,
mxx*myy*d.comp[compIndex].h*d.comp[compIndex].v)
+ }
+ }
}
+ d.b = bits{}
mcu, expectedRST := 0, uint8(rst0Marker)
var (
+ // b is the decoded coefficients, in natural (not zig-zag) order.
b block
dc [nColorComponent]int
+ // mx0 and my0 are the location of the current (in terms of 8x8 blocks).
+ // For example, with 4:2:0 chroma subsampling, the block whose top left
+ // pixel co-ordinates are (16, 8) is the third block in the first row:
+ // mx0 is 2 and my0 is 0, even though the pixel is in the second MCU.
+ // TODO(nigeltao): rename mx0 and my0 to bx and by?
+ mx0, my0 int
+ blockCount int
)
for my := 0; my < myy; my++ {
for mx := 0; mx < mxx; mx++ {
- for i := 0; i < d.nComp; i++ {
- qt := &d.quant[d.comp[i].tq]
- for j := 0; j < d.comp[i].h*d.comp[i].v; j++ {
- // TODO(nigeltao): make this a "var b block" once the compiler's
escape
- // analysis is good enough to allocate it on the stack, not the heap.
- // b is in natural (not zig-zag) order.
- b = block{}
+ for i := 0; i < nComp; i++ {
+ compIndex := scan[i].compIndex
+ qt := &d.quant[d.comp[compIndex].tq]
+ for j := 0; j < d.comp[compIndex].h*d.comp[compIndex].v; j++ {
+ // The blocks are traversed one MCU at a time. For 4:2:0 chroma
+ // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
+ // For a baseline 32x16 pixel image, the Y blocks visiting order is:
+ // 0 1 4 5
+ // 2 3 6 7
+ //
+ // For progressive images, the DC data blocks (zigStart == 0) are
traversed
+ // as above, but AC data blocks are traversed left to right, top to
bottom:
+ // 0 1 2 3
+ // 4 5 6 7
+ //
+ // To further complicate matters, there is no AC data for any blocks
that
+ // are inside the image at the MCU level but outside the image at the
pixel
+ // level. For example, a 24x16 pixel 4:2:0 progressive image consists
of
+ // two 16x16 MCUs. The earlier scans will process 8 Y blocks:
+ // 0 1 4 5
+ // 2 3 6 7
+ // The later scans will process only 6 Y blocks:
+ // 0 1 2
+ // 3 4 5
+ if zigStart == 0 {
+ mx0, my0 = d.comp[compIndex].h*mx, d.comp[compIndex].v*my
+ if h0 == 1 {
+ my0 += j
+ } else {
+ mx0 += j % 2
+ my0 += j / 2
+ }
+ } else {
+ q := mxx * d.comp[compIndex].h
+ mx0 = blockCount % q
+ my0 = blockCount / q
+ blockCount++
+ if mx0*8 >= d.width || my0*8 >= d.height {
+ continue
+ }
+ }
- // Decode the DC coefficient, as specified in section F.2.2.1.
- value, err := d.decodeHuffman(&d.huff[dcTable][scan[i].td])
- if err != nil {
- return err
+ // Load the previous partially decoded coefficients, if applicable.
+ if d.progressive {
+ b = d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0]
+ } else {
+ b = block{}
}
- if value > 16 {
- return UnsupportedError("excessive DC component")
- }
- dcDelta, err := d.receiveExtend(value)
- if err != nil {
- return err
- }
- dc[i] += dcDelta
- b[0] = dc[i] * qt[0]
- // Decode the AC coefficients, as specified in section F.2.2.2.
- for zig := 1; zig < blockSize; zig++ {
- value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta])
- if err != nil {
+ if ah != 0 {
+ if err := d.refine(&b, &d.huff[acTable][scan[i].ta], zigStart,
zigEnd, 1<<al); err != nil {
return err
}
- val0 := value >> 4
- val1 := value & 0x0f
- if val1 != 0 {
- zig += int(val0)
- if zig > blockSize {
- return FormatError("bad DCT index")
+ } else {
+ zig := zigStart
+ if zig == 0 {
+ zig++
+ // Decode the DC coefficient, as specified in section F.2.2.1.
+ value, err := d.decodeHuffman(&d.huff[dcTable][scan[i].td])
+ if err != nil {
+ return err
}
- ac, err := d.receiveExtend(val1)
+ if value > 16 {
+ return UnsupportedError("excessive DC component")
+ }
+ dcDelta, err := d.receiveExtend(value)
if err != nil {
return err
}
- b[unzig[zig]] = ac * qt[zig]
+ dc[compIndex] += dcDelta
+ b[0] = dc[compIndex] << al
+ }
+
+ if zig <= zigEnd && d.eobRun > 0 {
+ d.eobRun--
} else {
- if val0 != 0x0f {
- break
+ // Decode the AC coefficients, as specified in section F.2.2.2.
+ for ; zig <= zigEnd; zig++ {
+ value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta])
+ if err != nil {
+ return err
+ }
+ val0 := value >> 4
+ val1 := value & 0x0f
+ if val1 != 0 {
+ zig += int(val0)
+ if zig > zigEnd {
+ break
+ }
+ ac, err := d.receiveExtend(val1)
+ if err != nil {
+ return err
+ }
+ b[unzig[zig]] = ac << al
+ } else {
+ if val0 != 0x0f {
+ d.eobRun = uint16(1 << val0)
+ if val0 != 0 {
+ bits, err := d.decodeBits(int(val0))
+ if err != nil {
+ return err
+ }
+ d.eobRun |= uint16(bits)
+ }
+ d.eobRun--
+ break
+ }
+ zig += 0x0f
+ }
}
- zig += 0x0f
}
}
- // Perform the inverse DCT and store the MCU component to the image.
+ if d.progressive {
+ if zigEnd != blockSize-1 || al != 0 {
+ // We haven't completely decoded this 8x8 block. Save the
coefficients.
+ d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0] = b
+ // At this point, we could execute the rest of the loop body to
dequantize and
+ // perform the inverse DCT, to save early stages of a progressive
image to the
+ // *image.YCbCr buffers (the whole point of progressive encoding),
but in Go,
+ // the jpeg.Decode function does not return until the entire image
is decoded,
+ // so we "continue" here to avoid wasted computation.
+ continue
+ }
+ }
+
+ // Dequantize, perform the inverse DCT and store the block to the
image.
+ for zig := 0; zig < blockSize; zig++ {
+ b[unzig[zig]] *= qt[zig]
+ }
idct(&b)
dst, stride := []byte(nil), 0
if d.nComp == nGrayComponent {
- dst, stride = d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride
+ dst, stride = d.img1.Pix[8*(my0*d.img1.Stride+mx0):], d.img1.Stride
} else {
- switch i {
+ switch compIndex {
case 0:
- mx0, my0 := h0*mx, v0*my
- if h0 == 1 {
- my0 += j
- } else {
- mx0 += j % 2
- my0 += j / 2
- }
dst, stride = d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride
case 1:
- dst, stride = d.img3.Cb[8*(my*d.img3.CStride+mx):], d.img3.CStride
+ dst, stride = d.img3.Cb[8*(my0*d.img3.CStride+mx0):], d.img3.CStride
case 2:
- dst, stride =
d.img3.Cr[8*(my*d.img3.CStride+mx):], d.img3.CStride
+ dst, stride =
d.img3.Cr[8*(my0*d.img3.CStride+mx0):], d.img3.CStride
+ default:
+ return UnsupportedError("too many components")
}
}
// Level shift by +128, clip to [0, 255], and write to dst.
@@ -367,6 +505,8 @@
d.b = bits{}
// Reset the DC components, as per section F.2.1.3.1.
dc = [nColorComponent]int{}
+ // Reset the progressive decoder state, as per section G.1.2.2.
+ d.eobRun = 0
}
} // for mx
} // for my
@@ -439,13 +579,12 @@
}
switch {
- case marker == sof0Marker: // Start Of Frame (Baseline).
+ case marker == sof0Marker || marker == sof2Marker: // Start Of Frame.
+ d.progressive = marker == sof2Marker
err = d.processSOF(n)
if configOnly {
return nil, err
}
- case marker == sof2Marker: // Start Of Frame (Progressive).
- err = UnsupportedError("progressive mode")
case marker == dhtMarker: // Define Huffman Table.
err = d.processDHT(n)
case marker == dqtMarker: // Define Quantization Table.
=======================================
--- /src/pkg/image/jpeg/writer_test.go Sat Oct 6 17:30:47 2012
+++ /src/pkg/image/jpeg/writer_test.go Sun Oct 14 17:21:20 2012
@@ -170,23 +170,6 @@
}
}
}
-
-func BenchmarkDecode(b *testing.B) {
- b.StopTimer()
- data, err := ioutil.ReadFile("../testdata/video-001.jpeg")
- if err != nil {
- b.Fatal(err)
- }
- cfg, err := DecodeConfig(bytes.NewReader(data))
- if err != nil {
- b.Fatal(err)
- }
- b.SetBytes(int64(cfg.Width * cfg.Height * 4))
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- Decode(bytes.NewReader(data))
- }
-}
func BenchmarkEncode(b *testing.B) {
b.StopTimer()