summaryrefslogtreecommitdiff
path: root/libgo/go/image
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/image')
-rw-r--r--libgo/go/image/jpeg/dct_test.go291
-rw-r--r--libgo/go/image/jpeg/idct.go80
-rw-r--r--libgo/go/image/jpeg/reader.go59
-rw-r--r--libgo/go/image/jpeg/writer_test.go28
-rw-r--r--libgo/go/image/png/paeth.go70
-rw-r--r--libgo/go/image/png/paeth_test.go91
-rw-r--r--libgo/go/image/png/reader.go65
-rw-r--r--libgo/go/image/png/reader_test.go54
-rw-r--r--libgo/go/image/png/writer.go105
-rw-r--r--libgo/go/image/png/writer_test.go45
-rw-r--r--libgo/go/image/ycbcr.go9
-rw-r--r--libgo/go/image/ycbcr_test.go1
12 files changed, 733 insertions, 165 deletions
diff --git a/libgo/go/image/jpeg/dct_test.go b/libgo/go/image/jpeg/dct_test.go
new file mode 100644
index 00000000000..770e274bac0
--- /dev/null
+++ b/libgo/go/image/jpeg/dct_test.go
@@ -0,0 +1,291 @@
+// 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"
+ "math"
+ "math/rand"
+ "testing"
+)
+
+func benchmarkDCT(b *testing.B, f func(*block)) {
+ b.StopTimer()
+ blocks := make([]block, 0, b.N*len(testBlocks))
+ for i := 0; i < b.N; i++ {
+ blocks = append(blocks, testBlocks[:]...)
+ }
+ b.StartTimer()
+ for i := range blocks {
+ f(&blocks[i])
+ }
+}
+
+func BenchmarkFDCT(b *testing.B) {
+ benchmarkDCT(b, fdct)
+}
+
+func BenchmarkIDCT(b *testing.B) {
+ benchmarkDCT(b, idct)
+}
+
+func TestDCT(t *testing.T) {
+ blocks := make([]block, len(testBlocks))
+ copy(blocks, testBlocks[:])
+
+ // Append some randomly generated blocks of varying sparseness.
+ r := rand.New(rand.NewSource(123))
+ for i := 0; i < 100; i++ {
+ b := block{}
+ n := r.Int() % 64
+ for j := 0; j < n; j++ {
+ b[r.Int()%len(b)] = r.Int() % 256
+ }
+ blocks = append(blocks, b)
+ }
+
+ // Check that the FDCT and IDCT functions are inverses, after a scale and
+ // level shift. Scaling reduces the rounding errors in the conversion from
+ // floats to ints.
+ for i, b := range blocks {
+ got, want := b, b
+ for j := range got {
+ got[j] = (got[j] - 128) * 8
+ }
+ slowFDCT(&got)
+ slowIDCT(&got)
+ for j := range got {
+ got[j] = got[j]/8 + 128
+ }
+ if differ(&got, &want) {
+ t.Errorf("i=%d: IDCT(FDCT)\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
+ }
+ }
+
+ // Check that the optimized and slow FDCT implementations agree.
+ // The fdct function already does a scale and level shift.
+ for i, b := range blocks {
+ got, want := b, b
+ fdct(&got)
+ for j := range want {
+ want[j] = (want[j] - 128) * 8
+ }
+ slowFDCT(&want)
+ if differ(&got, &want) {
+ t.Errorf("i=%d: FDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
+ }
+ }
+
+ // Check that the optimized and slow IDCT implementations agree.
+ for i, b := range blocks {
+ got, want := b, b
+ idct(&got)
+ slowIDCT(&want)
+ if differ(&got, &want) {
+ t.Errorf("i=%d: IDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
+ }
+ }
+}
+
+// differ returns whether any pair-wise elements in b0 and b1 differ by 2 or
+// more. That tolerance is because there isn't a single definitive decoding of
+// a given JPEG image, even before the YCbCr to RGB conversion; implementations
+// can have different IDCT rounding errors.
+func differ(b0, b1 *block) bool {
+ for i := range b0 {
+ delta := b0[i] - b1[i]
+ if delta < -2 || +2 < delta {
+ return true
+ }
+ }
+ return false
+}
+
+// alpha returns 1 if i is 0 and returns √2 otherwise.
+func alpha(i int) float64 {
+ if i == 0 {
+ return 1
+ }
+ return math.Sqrt2
+}
+
+// slowFDCT performs the 8*8 2-dimensional forward discrete cosine transform:
+//
+// dst[u,v] = (1/8) * Σ_x Σ_y alpha(u) * alpha(v) * src[x,y] *
+// cos((π/2) * (2*x + 1) * u / 8) *
+// cos((π/2) * (2*y + 1) * v / 8)
+//
+// x and y are in pixel space, and u and v are in transform space.
+//
+// b acts as both dst and src.
+func slowFDCT(b *block) {
+ var dst [blockSize]float64
+ for v := 0; v < 8; v++ {
+ for u := 0; u < 8; u++ {
+ sum := 0.0
+ for y := 0; y < 8; y++ {
+ for x := 0; x < 8; x++ {
+ sum += alpha(u) * alpha(v) * float64(b[8*y+x]) *
+ math.Cos(math.Pi*float64((2*x+1)*u)/16) *
+ math.Cos(math.Pi*float64((2*y+1)*v)/16)
+ }
+ }
+ dst[8*v+u] = sum / 8
+ }
+ }
+ // Convert from float64 to int.
+ for i := range dst {
+ b[i] = int(dst[i] + 0.5)
+ }
+}
+
+// slowIDCT performs the 8*8 2-dimensional inverse discrete cosine transform:
+//
+// dst[x,y] = (1/8) * Σ_u Σ_v alpha(u) * alpha(v) * src[u,v] *
+// cos((π/2) * (2*x + 1) * u / 8) *
+// cos((π/2) * (2*y + 1) * v / 8)
+//
+// x and y are in pixel space, and u and v are in transform space.
+//
+// b acts as both dst and src.
+func slowIDCT(b *block) {
+ var dst [blockSize]float64
+ for y := 0; y < 8; y++ {
+ for x := 0; x < 8; x++ {
+ sum := 0.0
+ for v := 0; v < 8; v++ {
+ for u := 0; u < 8; u++ {
+ sum += alpha(u) * alpha(v) * float64(b[8*v+u]) *
+ math.Cos(math.Pi*float64((2*x+1)*u)/16) *
+ math.Cos(math.Pi*float64((2*y+1)*v)/16)
+ }
+ }
+ dst[8*y+x] = sum / 8
+ }
+ }
+ // Convert from float64 to int.
+ for i := range dst {
+ b[i] = int(dst[i] + 0.5)
+ }
+}
+
+func (b *block) String() string {
+ s := bytes.NewBuffer(nil)
+ fmt.Fprintf(s, "{\n")
+ for y := 0; y < 8; y++ {
+ fmt.Fprintf(s, "\t")
+ for x := 0; x < 8; x++ {
+ fmt.Fprintf(s, "0x%04x, ", uint16(b[8*y+x]))
+ }
+ fmt.Fprintln(s)
+ }
+ fmt.Fprintf(s, "}")
+ return s.String()
+}
+
+// testBlocks are the first 10 pre-IDCT blocks from ../testdata/video-001.jpeg.
+var testBlocks = [10]block{
+ {
+ 0x7f, 0xf6, 0x01, 0x07, 0xff, 0x00, 0x00, 0x00,
+ 0xf5, 0x01, 0xfa, 0x01, 0xfe, 0x00, 0x01, 0x00,
+ 0x05, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xff, 0xf8, 0x00, 0x01, 0xff, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00,
+ 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0xfe,
+ },
+ {
+ 0x29, 0x07, 0x00, 0xfc, 0x01, 0x01, 0x00, 0x00,
+ 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xfa, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff,
+ 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02,
+ },
+ {
+ 0xc5, 0xfa, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff,
+ 0x02, 0xff, 0x01, 0x00, 0x01, 0x00, 0xff, 0x00,
+ 0xff, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x86, 0x05, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00,
+ 0xf2, 0x06, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
+ 0xf6, 0xfa, 0xf9, 0x00, 0xff, 0x01, 0x00, 0x00,
+ 0xf9, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00,
+ },
+ {
+ 0x24, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00,
+ 0x08, 0xfd, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00,
+ 0x06, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x01,
+ },
+ {
+ 0xcd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff,
+ },
+ {
+ 0x81, 0xfe, 0x05, 0xff, 0x01, 0xff, 0x01, 0x00,
+ 0xef, 0xf9, 0x00, 0xf9, 0x00, 0xff, 0x00, 0xff,
+ 0x05, 0xf9, 0x00, 0xf8, 0x01, 0xff, 0x01, 0xff,
+ 0x00, 0xff, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01,
+ 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
+ },
+ {
+ 0x28, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x02, 0x01, 0x03, 0x00, 0xff, 0x00, 0x01,
+ 0xfe, 0x02, 0x01, 0x03, 0xff, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0xfd, 0x00, 0x01, 0x00, 0xff, 0x00,
+ 0x01, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01,
+ },
+ {
+ 0xdf, 0xf9, 0xfe, 0x00, 0x03, 0x01, 0xff, 0xff,
+ 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x01,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ },
+ {
+ 0x88, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff,
+ 0xe1, 0x06, 0x06, 0x01, 0xff, 0x00, 0x01, 0x00,
+ 0x08, 0x00, 0xfa, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x01, 0x00, 0xff, 0x01, 0xff, 0x00, 0x00,
+ 0xf5, 0xff, 0x00, 0x01, 0xff, 0x01, 0x01, 0x00,
+ 0xff, 0xff, 0x01, 0xff, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0x01,
+ 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
+ },
+}
diff --git a/libgo/go/image/jpeg/idct.go b/libgo/go/image/jpeg/idct.go
index b387dfdffd1..92ff1e4b41d 100644
--- a/libgo/go/image/jpeg/idct.go
+++ b/libgo/go/image/jpeg/idct.go
@@ -37,6 +37,10 @@ package jpeg
*
*/
+const blockSize = 64 // A DCT block is 8x8.
+
+type block [blockSize]int
+
const (
w1 = 2841 // 2048*sqrt(2)*cos(1*pi/16)
w2 = 2676 // 2048*sqrt(2)*cos(2*pi/16)
@@ -55,9 +59,7 @@ const (
r2 = 181 // 256/sqrt(2)
)
-// idct performs a 2-D Inverse Discrete Cosine Transformation, followed by a
-// +128 level shift and a clip to [0, 255], writing the results to dst.
-// stride is the number of elements between successive rows of dst.
+// idct performs a 2-D Inverse Discrete Cosine Transformation.
//
// The input coefficients should already have been multiplied by the
// appropriate quantization table. We use fixed-point computation, with the
@@ -67,33 +69,34 @@ const (
// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
-func idct(dst []byte, stride int, src *block) {
+func idct(src *block) {
// Horizontal 1-D IDCT.
for y := 0; y < 8; y++ {
+ y8 := y * 8
// If all the AC components are zero, then the IDCT is trivial.
- if src[y*8+1] == 0 && src[y*8+2] == 0 && src[y*8+3] == 0 &&
- src[y*8+4] == 0 && src[y*8+5] == 0 && src[y*8+6] == 0 && src[y*8+7] == 0 {
- dc := src[y*8+0] << 3
- src[y*8+0] = dc
- src[y*8+1] = dc
- src[y*8+2] = dc
- src[y*8+3] = dc
- src[y*8+4] = dc
- src[y*8+5] = dc
- src[y*8+6] = dc
- src[y*8+7] = dc
+ if src[y8+1] == 0 && src[y8+2] == 0 && src[y8+3] == 0 &&
+ src[y8+4] == 0 && src[y8+5] == 0 && src[y8+6] == 0 && src[y8+7] == 0 {
+ dc := src[y8+0] << 3
+ src[y8+0] = dc
+ src[y8+1] = dc
+ src[y8+2] = dc
+ src[y8+3] = dc
+ src[y8+4] = dc
+ src[y8+5] = dc
+ src[y8+6] = dc
+ src[y8+7] = dc
continue
}
// Prescale.
- x0 := (src[y*8+0] << 11) + 128
- x1 := src[y*8+4] << 11
- x2 := src[y*8+6]
- x3 := src[y*8+2]
- x4 := src[y*8+1]
- x5 := src[y*8+7]
- x6 := src[y*8+5]
- x7 := src[y*8+3]
+ x0 := (src[y8+0] << 11) + 128
+ x1 := src[y8+4] << 11
+ x2 := src[y8+6]
+ x3 := src[y8+2]
+ x4 := src[y8+1]
+ x5 := src[y8+7]
+ x6 := src[y8+5]
+ x7 := src[y8+3]
// Stage 1.
x8 := w7 * (x4 + x5)
@@ -123,14 +126,14 @@ func idct(dst []byte, stride int, src *block) {
x4 = (r2*(x4-x5) + 128) >> 8
// Stage 4.
- src[8*y+0] = (x7 + x1) >> 8
- src[8*y+1] = (x3 + x2) >> 8
- src[8*y+2] = (x0 + x4) >> 8
- src[8*y+3] = (x8 + x6) >> 8
- src[8*y+4] = (x8 - x6) >> 8
- src[8*y+5] = (x0 - x4) >> 8
- src[8*y+6] = (x3 - x2) >> 8
- src[8*y+7] = (x7 - x1) >> 8
+ src[y8+0] = (x7 + x1) >> 8
+ src[y8+1] = (x3 + x2) >> 8
+ src[y8+2] = (x0 + x4) >> 8
+ src[y8+3] = (x8 + x6) >> 8
+ src[y8+4] = (x8 - x6) >> 8
+ src[y8+5] = (x0 - x4) >> 8
+ src[y8+6] = (x3 - x2) >> 8
+ src[y8+7] = (x7 - x1) >> 8
}
// Vertical 1-D IDCT.
@@ -186,19 +189,4 @@ func idct(dst []byte, stride int, src *block) {
src[8*6+x] = (y3 - y2) >> 14
src[8*7+x] = (y7 - y1) >> 14
}
-
- // Level shift by +128, clip to [0, 255], and write to dst.
- for y := 0; y < 8; y++ {
- for x := 0; x < 8; x++ {
- c := src[y*8+x]
- if c < -128 {
- c = 0
- } else if c > 127 {
- c = 255
- } else {
- c += 128
- }
- dst[y*stride+x] = uint8(c)
- }
- }
}
diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go
index 8da3611919e..415b093281d 100644
--- a/libgo/go/image/jpeg/reader.go
+++ b/libgo/go/image/jpeg/reader.go
@@ -35,11 +35,7 @@ type component struct {
tq uint8 // Quantization table destination selector.
}
-type block [blockSize]int
-
const (
- blockSize = 64 // A DCT block is 8x8.
-
dcTable = 0
acTable = 1
maxTc = 1
@@ -51,7 +47,7 @@ const (
// A color JPEG image has Y, Cb and Cr components.
nColorComponent = 3
- // We only support 4:4:4, 4:2:2 and 4:2:0 downsampling, and therefore the
+ // We only support 4:4:4, 4:4:0, 4:2:2 and 4:2:0 downsampling, and therefore the
// number of luma samples per chroma sample is at most 2 in the horizontal
// and 2 in the vertical direction.
maxH = 2
@@ -96,6 +92,7 @@ type Reader interface {
type decoder struct {
r Reader
+ b bits
width, height int
img1 *image.Gray
img3 *image.YCbCr
@@ -104,7 +101,6 @@ type decoder struct {
comp [nColorComponent]component
huff [maxTc + 1][maxTh + 1]huffman
quant [maxTq + 1]block // Quantization tables, in zig-zag order.
- b bits
tmp [1024]byte
}
@@ -156,12 +152,12 @@ func (d *decoder) processSOF(n int) error {
if d.nComp == nGrayComponent {
continue
}
- // For color images, we only support 4:4:4, 4:2:2 or 4:2:0 chroma
+ // For color images, we only support 4:4:4, 4:4:0, 4:2:2 or 4:2:0 chroma
// downsampling ratios. This implies that the (h, v) values for the Y
- // component are either (1, 1), (2, 1) or (2, 2), and the (h, v)
+ // component are either (1, 1), (1, 2), (2, 1) or (2, 2), and the (h, v)
// values for the Cr and Cb components must be (1, 1).
if i == 0 {
- if hv != 0x11 && hv != 0x21 && hv != 0x22 {
+ if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 {
return UnsupportedError("luma downsample ratio")
}
} else if hv != 0x11 {
@@ -205,12 +201,14 @@ func (d *decoder) makeImg(h0, v0, mxx, myy int) {
return
}
var subsampleRatio image.YCbCrSubsampleRatio
- switch h0 * v0 {
- case 1:
+ switch {
+ case h0 == 1 && v0 == 1:
subsampleRatio = image.YCbCrSubsampleRatio444
- case 2:
+ case h0 == 1 && v0 == 2:
+ subsampleRatio = image.YCbCrSubsampleRatio440
+ case h0 == 2 && v0 == 1:
subsampleRatio = image.YCbCrSubsampleRatio422
- case 4:
+ case h0 == 2 && v0 == 2:
subsampleRatio = image.YCbCrSubsampleRatio420
default:
panic("unreachable")
@@ -311,18 +309,41 @@ func (d *decoder) processSOS(n int) error {
}
// Perform the inverse DCT and store the MCU component to the image.
+ idct(&b)
+ dst, stride := []byte(nil), 0
if d.nComp == nGrayComponent {
- idct(d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride, &b)
+ dst, stride = d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride
} else {
switch i {
case 0:
- mx0 := h0*mx + (j % 2)
- my0 := v0*my + (j / 2)
- idct(d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride, &b)
+ 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:
- idct(d.img3.Cb[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b)
+ dst, stride = d.img3.Cb[8*(my*d.img3.CStride+mx):], d.img3.CStride
case 2:
- idct(d.img3.Cr[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b)
+ dst, stride = d.img3.Cr[8*(my*d.img3.CStride+mx):], d.img3.CStride
+ }
+ }
+ // Level shift by +128, clip to [0, 255], and write to dst.
+ for y := 0; y < 8; y++ {
+ y8 := y * 8
+ yStride := y * stride
+ for x := 0; x < 8; x++ {
+ c := b[y8+x]
+ if c < -128 {
+ c = 0
+ } else if c > 127 {
+ c = 255
+ } else {
+ c += 128
+ }
+ dst[yStride+x] = uint8(c)
}
}
} // for j
diff --git a/libgo/go/image/jpeg/writer_test.go b/libgo/go/image/jpeg/writer_test.go
index 8732df8459a..90b89a7b0f1 100644
--- a/libgo/go/image/jpeg/writer_test.go
+++ b/libgo/go/image/jpeg/writer_test.go
@@ -171,24 +171,38 @@ func TestWriter(t *testing.T) {
}
}
-func BenchmarkEncodeRGBOpaque(b *testing.B) {
+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()
img := image.NewRGBA(image.Rect(0, 0, 640, 480))
- // Set all pixels to 0xFF alpha to force opaque mode.
bo := img.Bounds()
rnd := rand.New(rand.NewSource(123))
for y := bo.Min.Y; y < bo.Max.Y; y++ {
for x := bo.Min.X; x < bo.Max.X; x++ {
- img.Set(x, y, color.RGBA{
+ img.SetRGBA(x, y, color.RGBA{
uint8(rnd.Intn(256)),
uint8(rnd.Intn(256)),
uint8(rnd.Intn(256)),
- 255})
+ 255,
+ })
}
}
- if !img.Opaque() {
- b.Fatal("expected image to be opaque")
- }
b.SetBytes(640 * 480 * 4)
b.StartTimer()
options := &Options{Quality: 90}
diff --git a/libgo/go/image/png/paeth.go b/libgo/go/image/png/paeth.go
new file mode 100644
index 00000000000..37978aa662d
--- /dev/null
+++ b/libgo/go/image/png/paeth.go
@@ -0,0 +1,70 @@
+// 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 png
+
+// paeth implements the Paeth filter function, as per the PNG specification.
+func paeth(a, b, c uint8) uint8 {
+ // This is an optimized version of the sample code in the PNG spec.
+ // For example, the sample code starts with:
+ // p := int(a) + int(b) - int(c)
+ // pa := abs(p - int(a))
+ // but the optimized form uses fewer arithmetic operations:
+ // pa := int(b) - int(c)
+ // pa = abs(pa)
+ pc := int(c)
+ pa := int(b) - pc
+ pb := int(a) - pc
+ pc = pa + pb
+ if pa < 0 {
+ pa = -pa
+ }
+ if pb < 0 {
+ pb = -pb
+ }
+ if pc < 0 {
+ pc = -pc
+ }
+ if pa <= pb && pa <= pc {
+ return a
+ } else if pb <= pc {
+ return b
+ }
+ return c
+}
+
+// filterPaeth applies the Paeth filter to the cdat slice.
+// cdat is the current row's data, pdat is the previous row's data.
+func filterPaeth(cdat, pdat []byte, bytesPerPixel int) {
+ var a, b, c, pa, pb, pc int
+ for i := 0; i < bytesPerPixel; i++ {
+ a, c = 0, 0
+ for j := i; j < len(cdat); j += bytesPerPixel {
+ b = int(pdat[j])
+ pa = b - c
+ pb = a - c
+ pc = pa + pb
+ if pa < 0 {
+ pa = -pa
+ }
+ if pb < 0 {
+ pb = -pb
+ }
+ if pc < 0 {
+ pc = -pc
+ }
+ if pa <= pb && pa <= pc {
+ // No-op.
+ } else if pb <= pc {
+ a = b
+ } else {
+ a = c
+ }
+ a += int(cdat[j])
+ a &= 0xff
+ cdat[j] = uint8(a)
+ c = b
+ }
+ }
+}
diff --git a/libgo/go/image/png/paeth_test.go b/libgo/go/image/png/paeth_test.go
new file mode 100644
index 00000000000..bb084861ae9
--- /dev/null
+++ b/libgo/go/image/png/paeth_test.go
@@ -0,0 +1,91 @@
+// 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 png
+
+import (
+ "bytes"
+ "math/rand"
+ "testing"
+)
+
+func abs(x int) int {
+ if x < 0 {
+ return -x
+ }
+ return x
+}
+
+// slowPaeth is a slow but simple implementation of the Paeth function.
+// It is a straight port of the sample code in the PNG spec, section 9.4.
+func slowPaeth(a, b, c uint8) uint8 {
+ p := int(a) + int(b) - int(c)
+ pa := abs(p - int(a))
+ pb := abs(p - int(b))
+ pc := abs(p - int(c))
+ if pa <= pb && pa <= pc {
+ return a
+ } else if pb <= pc {
+ return b
+ }
+ return c
+}
+
+// slowFilterPaeth is a slow but simple implementation of func filterPaeth.
+func slowFilterPaeth(cdat, pdat []byte, bytesPerPixel int) {
+ 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])
+ }
+}
+
+func TestPaeth(t *testing.T) {
+ for a := 0; a < 256; a += 15 {
+ for b := 0; b < 256; b += 15 {
+ for c := 0; c < 256; c += 15 {
+ got := paeth(uint8(a), uint8(b), uint8(c))
+ want := slowPaeth(uint8(a), uint8(b), uint8(c))
+ if got != want {
+ t.Errorf("a, b, c = %d, %d, %d: got %d, want %d", a, b, c, got, want)
+ }
+ }
+ }
+ }
+}
+
+func BenchmarkPaeth(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ paeth(uint8(i>>16), uint8(i>>8), uint8(i))
+ }
+}
+
+func TestPaethDecode(t *testing.T) {
+ pdat0 := make([]byte, 32)
+ pdat1 := make([]byte, 32)
+ pdat2 := make([]byte, 32)
+ cdat0 := make([]byte, 32)
+ cdat1 := make([]byte, 32)
+ cdat2 := make([]byte, 32)
+ r := rand.New(rand.NewSource(1))
+ for bytesPerPixel := 1; bytesPerPixel <= 8; bytesPerPixel++ {
+ for i := 0; i < 100; i++ {
+ for j := range pdat0 {
+ pdat0[j] = uint8(r.Uint32())
+ cdat0[j] = uint8(r.Uint32())
+ }
+ copy(pdat1, pdat0)
+ copy(pdat2, pdat0)
+ copy(cdat1, cdat0)
+ copy(cdat2, cdat0)
+ filterPaeth(cdat1, pdat1, bytesPerPixel)
+ slowFilterPaeth(cdat2, pdat2, bytesPerPixel)
+ if !bytes.Equal(cdat1, cdat2) {
+ t.Errorf("bytesPerPixel: %d\npdat0: % x\ncdat0: % x\ngot: % x\nwant: % x", bytesPerPixel, pdat0, cdat0, cdat1, cdat2)
+ break
+ }
+ }
+ }
+}
diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go
index fe07d60a91a..b3901b2adf9 100644
--- a/libgo/go/image/png/reader.go
+++ b/libgo/go/image/png/reader.go
@@ -98,13 +98,6 @@ type UnsupportedError string
func (e UnsupportedError) Error() string { return "png: unsupported feature: " + string(e) }
-func abs(x int) int {
- if x < 0 {
- return -x
- }
- return x
-}
-
func min(a, b int) int {
if a < b {
return a
@@ -233,7 +226,7 @@ func (d *decoder) parsetRNS(length uint32) error {
}
for i := 0; i < n; i++ {
rgba := d.palette[i].(color.RGBA)
- d.palette[i] = color.RGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
+ d.palette[i] = color.NRGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
}
case cbGA8, cbGA16, cbTCA8, cbTCA16:
return FormatError("tRNS, color type mismatch")
@@ -241,20 +234,6 @@ func (d *decoder) parsetRNS(length uint32) error {
return d.verifyChecksum()
}
-// The Paeth filter function, as per the PNG specification.
-func paeth(a, b, c uint8) uint8 {
- p := int(a) + int(b) - int(c)
- pa := abs(p - int(a))
- pb := abs(p - int(b))
- pc := abs(p - int(c))
- if pa <= pb && pa <= pc {
- return a
- } else if pb <= pc {
- return b
- }
- return c
-}
-
// Read presents one or more IDAT chunks as one continuous stream (minus the
// intermediate chunk headers and footers). If the PNG data looked like:
// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2
@@ -301,6 +280,7 @@ func (d *decoder) decode() (image.Image, error) {
defer r.Close()
bitsPerPixel := 0
maxPalette := uint8(0)
+ pixOffset := 0
var (
gray *image.Gray
rgba *image.RGBA
@@ -375,8 +355,8 @@ func (d *decoder) decode() (image.Image, error) {
cdat[i] += cdat[i-bytesPerPixel]
}
case ftUp:
- for i := 0; i < len(cdat); i++ {
- cdat[i] += pdat[i]
+ for i, p := range pdat {
+ cdat[i] += p
}
case ftAverage:
for i := 0; i < bytesPerPixel; i++ {
@@ -386,12 +366,7 @@ func (d *decoder) decode() (image.Image, error) {
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])
- }
+ filterPaeth(cdat, pdat, bytesPerPixel)
default:
return nil, FormatError("bad filter type")
}
@@ -423,18 +398,24 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbG8:
- for x := 0; x < d.width; x++ {
- gray.SetGray(x, y, color.Gray{cdat[x]})
- }
+ 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++ {
- rgba.SetRGBA(x, y, color.RGBA{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff})
+ 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]
@@ -472,16 +453,18 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbP8:
- for x := 0; x < d.width; x++ {
- if cdat[x] > maxPalette {
- return nil, FormatError("palette index out of range")
+ if maxPalette != 255 {
+ 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])
}
+ copy(paletted.Pix[pixOffset:], cdat)
+ pixOffset += paletted.Stride
case cbTCA8:
- for x := 0; x < d.width; x++ {
- nrgba.SetNRGBA(x, y, color.NRGBA{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]})
- }
+ 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])
diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go
index 24c4ea44808..8223f521cc3 100644
--- a/libgo/go/image/png/reader_test.go
+++ b/libgo/go/image/png/reader_test.go
@@ -10,6 +10,7 @@ import (
"image"
"image/color"
"io"
+ "io/ioutil"
"os"
"strings"
"testing"
@@ -106,13 +107,18 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
lastAlpha := -1
io.WriteString(w, "PLTE {\n")
for i, c := range cpm {
- r, g, b, a := c.RGBA()
- if a != 0xffff {
+ var r, g, b, a uint8
+ switch c := c.(type) {
+ case color.RGBA:
+ r, g, b, a = c.R, c.G, c.B, 0xff
+ case color.NRGBA:
+ r, g, b, a = c.R, c.G, c.B, c.A
+ default:
+ panic("unknown palette color type")
+ }
+ if a != 0xff {
lastAlpha = i
}
- r >>= 8
- g >>= 8
- b >>= 8
fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
}
io.WriteString(w, "}\n")
@@ -267,3 +273,41 @@ func TestReaderError(t *testing.T) {
}
}
}
+
+func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ b.Fatal(err)
+ }
+ s := string(data)
+ cfg, err := DecodeConfig(strings.NewReader(s))
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(cfg.Width * cfg.Height * bytesPerPixel))
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Decode(strings.NewReader(s))
+ }
+}
+
+func BenchmarkDecodeGray(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchGray.png", 1)
+}
+
+func BenchmarkDecodeNRGBAGradient(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchNRGBA-gradient.png", 4)
+}
+
+func BenchmarkDecodeNRGBAOpaque(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchNRGBA-opaque.png", 4)
+}
+
+func BenchmarkDecodePaletted(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchPaletted.png", 1)
+}
+
+func BenchmarkDecodeRGB(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchRGB.png", 4)
+}
diff --git a/libgo/go/image/png/writer.go b/libgo/go/image/png/writer.go
index 57c03792b59..093d47193ba 100644
--- a/libgo/go/image/png/writer.go
+++ b/libgo/go/image/png/writer.go
@@ -21,7 +21,7 @@ type encoder struct {
err error
header [8]byte
footer [4]byte
- tmp [3 * 256]byte
+ tmp [4 * 256]byte
}
// Big-endian.
@@ -70,7 +70,7 @@ func (e *encoder) writeChunk(b []byte, name string) {
e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
return
}
- writeUint32(e.header[0:4], n)
+ writeUint32(e.header[:4], n)
e.header[4] = name[0]
e.header[5] = name[1]
e.header[6] = name[2]
@@ -78,9 +78,9 @@ func (e *encoder) writeChunk(b []byte, name string) {
crc := crc32.NewIEEE()
crc.Write(e.header[4:8])
crc.Write(b)
- writeUint32(e.footer[0:4], crc.Sum32())
+ writeUint32(e.footer[:4], crc.Sum32())
- _, e.err = e.w.Write(e.header[0:8])
+ _, e.err = e.w.Write(e.header[:8])
if e.err != nil {
return
}
@@ -88,7 +88,7 @@ func (e *encoder) writeChunk(b []byte, name string) {
if e.err != nil {
return
}
- _, e.err = e.w.Write(e.footer[0:4])
+ _, e.err = e.w.Write(e.footer[:4])
}
func (e *encoder) writeIHDR() {
@@ -122,36 +122,29 @@ func (e *encoder) writeIHDR() {
e.tmp[10] = 0 // default compression method
e.tmp[11] = 0 // default filter method
e.tmp[12] = 0 // non-interlaced
- e.writeChunk(e.tmp[0:13], "IHDR")
+ e.writeChunk(e.tmp[:13], "IHDR")
}
-func (e *encoder) writePLTE(p color.Palette) {
+func (e *encoder) writePLTEAndTRNS(p color.Palette) {
if len(p) < 1 || len(p) > 256 {
e.err = FormatError("bad palette length: " + strconv.Itoa(len(p)))
return
}
- for i, c := range p {
- r, g, b, _ := c.RGBA()
- e.tmp[3*i+0] = uint8(r >> 8)
- e.tmp[3*i+1] = uint8(g >> 8)
- e.tmp[3*i+2] = uint8(b >> 8)
- }
- e.writeChunk(e.tmp[0:3*len(p)], "PLTE")
-}
-
-func (e *encoder) maybeWritetRNS(p color.Palette) {
last := -1
for i, c := range p {
- _, _, _, a := c.RGBA()
- if a != 0xffff {
+ c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
+ e.tmp[3*i+0] = c1.R
+ e.tmp[3*i+1] = c1.G
+ e.tmp[3*i+2] = c1.B
+ if c1.A != 0xff {
last = i
}
- e.tmp[i] = uint8(a >> 8)
+ e.tmp[3*256+i] = c1.A
}
- if last == -1 {
- return
+ e.writeChunk(e.tmp[:3*len(p)], "PLTE")
+ if last != -1 {
+ e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
}
- e.writeChunk(e.tmp[:last+1], "tRNS")
}
// An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
@@ -297,26 +290,42 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
pr := make([]uint8, 1+bpp*b.Dx())
+ gray, _ := m.(*image.Gray)
+ rgba, _ := m.(*image.RGBA)
+ paletted, _ := m.(*image.Paletted)
+ nrgba, _ := m.(*image.NRGBA)
+
for y := b.Min.Y; y < b.Max.Y; y++ {
// Convert from colors to bytes.
i := 1
switch cb {
case cbG8:
- for x := b.Min.X; x < b.Max.X; x++ {
- c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
- cr[0][i] = c.Y
- i++
+ if gray != nil {
+ offset := (y - b.Min.Y) * gray.Stride
+ copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
+ } else {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
+ cr[0][i] = c.Y
+ i++
+ }
}
case cbTC8:
// We have previously verified that the alpha value is fully opaque.
cr0 := cr[0]
- if rgba, _ := m.(*image.RGBA); rgba != nil {
- j0 := (y - b.Min.Y) * rgba.Stride
+ stride, pix := 0, []byte(nil)
+ if rgba != nil {
+ stride, pix = rgba.Stride, rgba.Pix
+ } else if nrgba != nil {
+ stride, pix = nrgba.Stride, nrgba.Pix
+ }
+ if stride != 0 {
+ j0 := (y - b.Min.Y) * stride
j1 := j0 + b.Dx()*4
for j := j0; j < j1; j += 4 {
- cr0[i+0] = rgba.Pix[j+0]
- cr0[i+1] = rgba.Pix[j+1]
- cr0[i+2] = rgba.Pix[j+2]
+ cr0[i+0] = pix[j+0]
+ cr0[i+1] = pix[j+1]
+ cr0[i+2] = pix[j+2]
i += 3
}
} else {
@@ -329,9 +338,9 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
}
case cbP8:
- if p, _ := m.(*image.Paletted); p != nil {
- offset := (y - b.Min.Y) * p.Stride
- copy(cr[0][1:], p.Pix[offset:offset+b.Dx()])
+ if paletted != nil {
+ offset := (y - b.Min.Y) * paletted.Stride
+ copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
} else {
pi := m.(image.PalettedImage)
for x := b.Min.X; x < b.Max.X; x++ {
@@ -340,14 +349,19 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
}
case cbTCA8:
- // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
- for x := b.Min.X; x < b.Max.X; x++ {
- c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
- cr[0][i+0] = c.R
- cr[0][i+1] = c.G
- cr[0][i+2] = c.B
- cr[0][i+3] = c.A
- i += 4
+ if nrgba != nil {
+ offset := (y - b.Min.Y) * nrgba.Stride
+ copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
+ } else {
+ // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
+ for x := b.Min.X; x < b.Max.X; x++ {
+ c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
+ cr[0][i+0] = c.R
+ cr[0][i+1] = c.G
+ cr[0][i+2] = c.B
+ cr[0][i+3] = c.A
+ i += 4
+ }
}
case cbG16:
for x := b.Min.X; x < b.Max.X; x++ {
@@ -412,7 +426,7 @@ func (e *encoder) writeIDATs() {
e.err = bw.Flush()
}
-func (e *encoder) writeIEND() { e.writeChunk(e.tmp[0:0], "IEND") }
+func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
// Encode writes the Image m to w in PNG format. Any Image may be encoded, but
// images that are not image.NRGBA might be encoded lossily.
@@ -460,8 +474,7 @@ func Encode(w io.Writer, m image.Image) error {
_, e.err = io.WriteString(w, pngHeader)
e.writeIHDR()
if pal != nil {
- e.writePLTE(pal)
- e.maybeWritetRNS(pal)
+ e.writePLTEAndTRNS(pal)
}
e.writeIDATs()
e.writeIEND()
diff --git a/libgo/go/image/png/writer_test.go b/libgo/go/image/png/writer_test.go
index 644c4fb44b3..3116fc9ff94 100644
--- a/libgo/go/image/png/writer_test.go
+++ b/libgo/go/image/png/writer_test.go
@@ -101,6 +101,49 @@ func TestSubImage(t *testing.T) {
}
}
+func BenchmarkEncodeGray(b *testing.B) {
+ b.StopTimer()
+ img := image.NewGray(image.Rect(0, 0, 640, 480))
+ b.SetBytes(640 * 480 * 1)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
+func BenchmarkEncodeNRGBOpaque(b *testing.B) {
+ b.StopTimer()
+ img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
+ // Set all pixels to 0xFF alpha to force opaque mode.
+ bo := img.Bounds()
+ for y := bo.Min.Y; y < bo.Max.Y; y++ {
+ for x := bo.Min.X; x < bo.Max.X; x++ {
+ img.Set(x, y, color.NRGBA{0, 0, 0, 255})
+ }
+ }
+ if !img.Opaque() {
+ b.Fatal("expected image to be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
+func BenchmarkEncodeNRGBA(b *testing.B) {
+ b.StopTimer()
+ img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
+ if img.Opaque() {
+ b.Fatal("expected image not to be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
func BenchmarkEncodePaletted(b *testing.B) {
b.StopTimer()
img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{
@@ -138,7 +181,7 @@ func BenchmarkEncodeRGBA(b *testing.B) {
b.StopTimer()
img := image.NewRGBA(image.Rect(0, 0, 640, 480))
if img.Opaque() {
- b.Fatal("expected image to not be opaque")
+ b.Fatal("expected image not to be opaque")
}
b.SetBytes(640 * 480 * 4)
b.StartTimer()
diff --git a/libgo/go/image/ycbcr.go b/libgo/go/image/ycbcr.go
index c1a0b666f83..5b73bef7895 100644
--- a/libgo/go/image/ycbcr.go
+++ b/libgo/go/image/ycbcr.go
@@ -15,6 +15,7 @@ const (
YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota
YCbCrSubsampleRatio422
YCbCrSubsampleRatio420
+ YCbCrSubsampleRatio440
)
func (s YCbCrSubsampleRatio) String() string {
@@ -25,6 +26,8 @@ func (s YCbCrSubsampleRatio) String() string {
return "YCbCrSubsampleRatio422"
case YCbCrSubsampleRatio420:
return "YCbCrSubsampleRatio420"
+ case YCbCrSubsampleRatio440:
+ return "YCbCrSubsampleRatio440"
}
return "YCbCrSubsampleRatioUnknown"
}
@@ -39,6 +42,7 @@ func (s YCbCrSubsampleRatio) String() string {
// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
+// For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2.
type YCbCr struct {
Y, Cb, Cr []uint8
YStride int
@@ -82,6 +86,8 @@ func (p *YCbCr) COffset(x, y int) int {
return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2)
case YCbCrSubsampleRatio420:
return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2)
+ case YCbCrSubsampleRatio440:
+ return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X)
}
// Default to 4:4:4 subsampling.
return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X)
@@ -126,6 +132,9 @@ func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr {
case YCbCrSubsampleRatio420:
cw = (r.Max.X+1)/2 - r.Min.X/2
ch = (r.Max.Y+1)/2 - r.Min.Y/2
+ case YCbCrSubsampleRatio440:
+ cw = w
+ ch = (r.Max.Y+1)/2 - r.Min.Y/2
default:
// Default to 4:4:4 subsampling.
cw = w
diff --git a/libgo/go/image/ycbcr_test.go b/libgo/go/image/ycbcr_test.go
index b2373f79ba3..7d1043dd395 100644
--- a/libgo/go/image/ycbcr_test.go
+++ b/libgo/go/image/ycbcr_test.go
@@ -37,6 +37,7 @@ func TestYCbCr(t *testing.T) {
YCbCrSubsampleRatio444,
YCbCrSubsampleRatio422,
YCbCrSubsampleRatio420,
+ YCbCrSubsampleRatio440,
}
deltas := []Point{
Pt(0, 0),