diff options
Diffstat (limited to 'libgo/go/image')
-rw-r--r-- | libgo/go/image/color/color.go | 2 | ||||
-rw-r--r-- | libgo/go/image/color/palette/gen.go | 2 | ||||
-rw-r--r-- | libgo/go/image/color/palette/palette.go | 2 | ||||
-rw-r--r-- | libgo/go/image/color/ycbcr.go | 110 | ||||
-rw-r--r-- | libgo/go/image/color/ycbcr_test.go | 44 | ||||
-rw-r--r-- | libgo/go/image/decode_test.go | 1 | ||||
-rw-r--r-- | libgo/go/image/draw/draw.go | 10 | ||||
-rw-r--r-- | libgo/go/image/gif/reader.go | 27 | ||||
-rw-r--r-- | libgo/go/image/gif/reader_test.go | 80 | ||||
-rw-r--r-- | libgo/go/image/internal/imageutil/gen.go | 59 | ||||
-rw-r--r-- | libgo/go/image/internal/imageutil/impl.go | 232 | ||||
-rw-r--r-- | libgo/go/image/jpeg/reader.go | 6 | ||||
-rw-r--r-- | libgo/go/image/jpeg/scan.go | 128 | ||||
-rw-r--r-- | libgo/go/image/png/reader.go | 7 | ||||
-rw-r--r-- | libgo/go/image/png/reader_test.go | 27 | ||||
-rw-r--r-- | libgo/go/image/testdata/video-001.progressive.truncated.jpeg | bin | 0 -> 7456 bytes | |||
-rw-r--r-- | libgo/go/image/testdata/video-001.progressive.truncated.png | bin | 0 -> 23616 bytes |
17 files changed, 505 insertions, 232 deletions
diff --git a/libgo/go/image/color/color.go b/libgo/go/image/color/color.go index cae059b6daa..104433974ea 100644 --- a/libgo/go/image/color/color.go +++ b/libgo/go/image/color/color.go @@ -147,7 +147,7 @@ type Model interface { func ModelFunc(f func(Color) Color) Model { // Note: using *modelFunc as the implementation // means that callers can still use comparisons - // like m == RGBAModel. This is not possible if + // like m == RGBAModel. This is not possible if // we use the func value directly, because funcs // are no longer comparable. return &modelFunc{f} diff --git a/libgo/go/image/color/palette/gen.go b/libgo/go/image/color/palette/gen.go index 2b5fdaaf2b3..57718e6a0c5 100644 --- a/libgo/go/image/color/palette/gen.go +++ b/libgo/go/image/color/palette/gen.go @@ -89,7 +89,7 @@ func printPlan9(w io.Writer) { fmt.Fprintln(w, "// of continuous tones.") fmt.Fprintln(w, "//") fmt.Fprintln(w, "// This palette was used in the Plan 9 Operating System, described at") - fmt.Fprintln(w, "// http://plan9.bell-labs.com/magic/man2html/6/color") + fmt.Fprintln(w, "// https://9p.io/magic/man2html/6/color") fmt.Fprintln(w, "var Plan9 = []color.Color{") for _, line := range lines { fmt.Fprintln(w, line) diff --git a/libgo/go/image/color/palette/palette.go b/libgo/go/image/color/palette/palette.go index 0bf2c8e1aa5..b6954145204 100644 --- a/libgo/go/image/color/palette/palette.go +++ b/libgo/go/image/color/palette/palette.go @@ -19,7 +19,7 @@ import "image/color" // of continuous tones. // // This palette was used in the Plan 9 Operating System, described at -// http://plan9.bell-labs.com/magic/man2html/6/color +// https://9p.io/magic/man2html/6/color var Plan9 = []color.Color{ color.RGBA{0x00, 0x00, 0x00, 0xff}, color.RGBA{0x00, 0x00, 0x44, 0xff}, diff --git a/libgo/go/image/color/ycbcr.go b/libgo/go/image/color/ycbcr.go index 904434f6a3d..3df5d3675d6 100644 --- a/libgo/go/image/color/ycbcr.go +++ b/libgo/go/image/color/ycbcr.go @@ -15,24 +15,37 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) { r1 := int32(r) g1 := int32(g) b1 := int32(b) + + // yy is in range [0,0xff]. yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16 - cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16 - cr := (32768*r1 - 27440*g1 - 5328*b1 + 257<<15) >> 16 - if yy < 0 { - yy = 0 - } else if yy > 0xff { - yy = 0xff - } - if cb < 0 { - cb = 0 - } else if cb > 0xff { - cb = 0xff + + // The bit twiddling below is equivalent to + // + // cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16 + // if cb < 0 { + // cb = 0 + // } else if cb > 0xff { + // cb = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute cr uses a similar pattern. + cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15 + if uint32(cb)&0xff000000 == 0 { + cb >>= 16 + } else { + cb = ^(cb >> 31) } - if cr < 0 { - cr = 0 - } else if cr > 0xff { - cr = 0xff + + cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15 + if uint32(cr)&0xff000000 == 0 { + cr >>= 16 + } else { + cr = ^(cr >> 31) } + return uint8(yy), uint8(cb), uint8(cr) } @@ -44,27 +57,44 @@ func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) { // B = Y' + 1.77200*(Cb-128) // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. - yy1 := int32(y) * 0x10100 // Convert 0x12 to 0x121200. + yy1 := int32(y) * 0x010100 // Convert 0x12 to 0x121200. cb1 := int32(cb) - 128 cr1 := int32(cr) - 128 - r := (yy1 + 91881*cr1) >> 16 - g := (yy1 - 22554*cb1 - 46802*cr1) >> 16 - b := (yy1 + 116130*cb1) >> 16 - if r < 0 { - r = 0 - } else if r > 0xff { - r = 0xff + + // The bit twiddling below is equivalent to + // + // r := (yy1 + 91881*cr1) >> 16 + // if r < 0 { + // r = 0 + // } else if r > 0xff { + // r = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute g and b uses a similar pattern. + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) } - if g < 0 { - g = 0 - } else if g > 0xff { - g = 0xff + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) } - if b < 0 { - b = 0 - } else if b > 0xff { - b = 0xff + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) } + return uint8(r), uint8(g), uint8(b) } @@ -220,10 +250,10 @@ func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) { // CMYKToRGB converts a CMYK quadruple to an RGB triple. func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) { - w := uint32(0xffff - uint32(k)*0x101) - r := uint32(0xffff-uint32(c)*0x101) * w / 0xffff - g := uint32(0xffff-uint32(m)*0x101) * w / 0xffff - b := uint32(0xffff-uint32(y)*0x101) * w / 0xffff + w := 0xffff - uint32(k)*0x101 + r := (0xffff - uint32(c)*0x101) * w / 0xffff + g := (0xffff - uint32(m)*0x101) * w / 0xffff + b := (0xffff - uint32(y)*0x101) * w / 0xffff return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8) } @@ -239,11 +269,11 @@ func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) { // This code is a copy of the CMYKToRGB function above, except that it // returns values in the range [0, 0xffff] instead of [0, 0xff]. - w := uint32(0xffff - uint32(c.K)*0x101) - r := uint32(0xffff-uint32(c.C)*0x101) * w / 0xffff - g := uint32(0xffff-uint32(c.M)*0x101) * w / 0xffff - b := uint32(0xffff-uint32(c.Y)*0x101) * w / 0xffff - return uint32(r), uint32(g), uint32(b), 0xffff + w := 0xffff - uint32(c.K)*0x101 + r := (0xffff - uint32(c.C)*0x101) * w / 0xffff + g := (0xffff - uint32(c.M)*0x101) * w / 0xffff + b := (0xffff - uint32(c.Y)*0x101) * w / 0xffff + return r, g, b, 0xffff } // CMYKModel is the Model for CMYK colors. diff --git a/libgo/go/image/color/ycbcr_test.go b/libgo/go/image/color/ycbcr_test.go index f5e7cbf3358..561699f4e0e 100644 --- a/libgo/go/image/color/ycbcr_test.go +++ b/libgo/go/image/color/ycbcr_test.go @@ -171,3 +171,47 @@ func TestPalette(t *testing.T) { t.Errorf("got %v, want %v", got, want) } } + +var sink uint8 + +func BenchmarkYCbCrToRGB(b *testing.B) { + // YCbCrToRGB does saturating arithmetic. + // Low, middle, and high values can take + // different paths through the generated code. + b.Run("0", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink, sink, sink = YCbCrToRGB(0, 0, 0) + } + }) + b.Run("128", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink, sink, sink = YCbCrToRGB(128, 128, 128) + } + }) + b.Run("255", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink, sink, sink = YCbCrToRGB(255, 255, 255) + } + }) +} + +func BenchmarkRGBToYCbCr(b *testing.B) { + // RGBToYCbCr does saturating arithmetic. + // Different values can take different paths + // through the generated code. + b.Run("0", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink, sink, sink = RGBToYCbCr(0, 0, 0) + } + }) + b.Run("Cb", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink, sink, sink = RGBToYCbCr(0, 0, 255) + } + }) + b.Run("Cr", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink, sink, sink = RGBToYCbCr(255, 0, 0) + } + }) +} diff --git a/libgo/go/image/decode_test.go b/libgo/go/image/decode_test.go index d16ef8a1a4d..85e235e729b 100644 --- a/libgo/go/image/decode_test.go +++ b/libgo/go/image/decode_test.go @@ -36,6 +36,7 @@ var imageTests = []imageTest{ {"testdata/video-001.221212.png", "testdata/video-001.221212.jpeg", 8 << 8}, {"testdata/video-001.cmyk.png", "testdata/video-001.cmyk.jpeg", 8 << 8}, {"testdata/video-001.rgb.png", "testdata/video-001.rgb.jpeg", 8 << 8}, + {"testdata/video-001.progressive.truncated.png", "testdata/video-001.progressive.truncated.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}, diff --git a/libgo/go/image/draw/draw.go b/libgo/go/image/draw/draw.go index e47c48d961e..6a16cd39cf8 100644 --- a/libgo/go/image/draw/draw.go +++ b/libgo/go/image/draw/draw.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2009 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. @@ -634,10 +634,10 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, if !floydSteinberg { continue } - er -= int32(palette[bestIndex][0]) - eg -= int32(palette[bestIndex][1]) - eb -= int32(palette[bestIndex][2]) - ea -= int32(palette[bestIndex][3]) + er -= palette[bestIndex][0] + eg -= palette[bestIndex][1] + eb -= palette[bestIndex][2] + ea -= palette[bestIndex][3] } else { out.R = uint16(er) diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go index 6a133124ad5..6181a946fad 100644 --- a/libgo/go/image/gif/reader.go +++ b/libgo/go/image/gif/reader.go @@ -96,7 +96,7 @@ type decoder struct { // blockReader parses the block structure of GIF image data, which // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the // reader given to the LZW decoder, which is thus immune to the -// blocking. After the LZW decoder completes, there will be a 0-byte +// blocking. After the LZW decoder completes, there will be a 0-byte // block remaining (0, ()), which is consumed when checking that the // blockReader is exhausted. type blockReader struct { @@ -178,12 +178,25 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { } m.Palette = d.globalColorTable } - if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) { + if d.hasTransparentIndex { if !useLocalColorTable { // Clone the global color table. m.Palette = append(color.Palette(nil), d.globalColorTable...) } - m.Palette[d.transparentIndex] = color.RGBA{} + if ti := int(d.transparentIndex); ti < len(m.Palette) { + m.Palette[ti] = color.RGBA{} + } else { + // The transparentIndex is out of range, which is an error + // according to the spec, but Firefox and Google Chrome + // seem OK with this, so we enlarge the palette with + // transparent colors. See golang.org/issue/15059. + p := make(color.Palette, ti+1) + copy(p, m.Palette) + for i := len(m.Palette); i < len(p); i++ { + p[i] = color.RGBA{} + } + m.Palette = p + } } litWidth, err := d.r.ReadByte() if err != nil { @@ -210,7 +223,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { // for an image". In practice, though, giflib (a widely used C // library) does not enforce this, so we also accept lzwr returning // io.ErrUnexpectedEOF (meaning that the encoded stream hit io.EOF - // before the LZW decoder saw an explict end code), provided that + // before the LZW decoder saw an explicit end code), provided that // the io.ReadFull call above successfully read len(m.Pix) bytes. // See https://golang.org/issue/9856 for an example GIF. if n, err := lzwr.Read(d.tmp[:1]); n != 0 || (err != io.EOF && err != io.ErrUnexpectedEOF) { @@ -349,6 +362,9 @@ func (d *decoder) readGraphicControl() error { if _, err := io.ReadFull(d.r, d.tmp[:6]); err != nil { return fmt.Errorf("gif: can't read graphic control: %s", err) } + if d.tmp[0] != 4 { + return fmt.Errorf("gif: invalid graphic control extension block size: %d", d.tmp[0]) + } flags := d.tmp[1] d.disposalMethod = (flags & gcDisposalMethodMask) >> 2 d.delayTime = int(d.tmp[2]) | int(d.tmp[3])<<8 @@ -356,6 +372,9 @@ func (d *decoder) readGraphicControl() error { d.transparentIndex = d.tmp[4] d.hasTransparentIndex = true } + if d.tmp[5] != 0 { + return fmt.Errorf("gif: invalid graphic control extension block terminator: %d", d.tmp[5]) + } return nil } diff --git a/libgo/go/image/gif/reader_test.go b/libgo/go/image/gif/reader_test.go index c294195b6f7..90c81493cba 100644 --- a/libgo/go/image/gif/reader_test.go +++ b/libgo/go/image/gif/reader_test.go @@ -22,12 +22,16 @@ const ( trailerStr = "\x3b" ) -// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. -func lzwEncode(n int) []byte { +// lzwEncode returns an LZW encoding (with 2-bit literals) of in. +func lzwEncode(in []byte) []byte { b := &bytes.Buffer{} w := lzw.NewWriter(b, lzw.LSB, 2) - w.Write(make([]byte, n)) - w.Close() + if _, err := w.Write(in); err != nil { + panic(err) + } + if err := w.Close(); err != nil { + panic(err) + } return b.Bytes() } @@ -53,7 +57,7 @@ func TestDecode(t *testing.T) { // byte, and 2-bit LZW literals. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") if tc.nPix > 0 { - enc := lzwEncode(tc.nPix) + enc := lzwEncode(make([]byte, tc.nPix)) if len(enc) > 0xff { t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc)) continue @@ -97,13 +101,13 @@ func TestTransparentIndex(t *testing.T) { for transparentIndex := 0; transparentIndex < 3; transparentIndex++ { if transparentIndex < 2 { // Write the graphic control for the transparent index. - b.WriteString("\x21\xf9\x00\x01\x00\x00") + b.WriteString("\x21\xf9\x04\x01\x00\x00") b.WriteByte(byte(transparentIndex)) b.WriteByte(0) } // Write an image with bounds 2x1, as per TestDecode. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") - enc := lzwEncode(2) + enc := lzwEncode([]byte{0x00, 0x00}) if len(enc) > 0xff { t.Fatalf("compressed length %d is too large", len(enc)) } @@ -196,21 +200,13 @@ func TestNoPalette(t *testing.T) { b.WriteString(headerStr[:len(headerStr)-3]) b.WriteString("\x00\x00\x00") // No global palette. - // Image descriptor: 2x1, no local palette. + // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") // Encode the pixels: neither is in range, because there is no palette. - pix := []byte{0, 3} - enc := &bytes.Buffer{} - w := lzw.NewWriter(enc, lzw.LSB, 2) - if _, err := w.Write(pix); err != nil { - t.Fatalf("Write: %v", err) - } - if err := w.Close(); err != nil { - t.Fatalf("Close: %v", err) - } - b.WriteByte(byte(len(enc.Bytes()))) - b.Write(enc.Bytes()) + enc := lzwEncode([]byte{0x00, 0x03}) + b.WriteByte(byte(len(enc))) + b.Write(enc) b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteString(trailerStr) @@ -226,21 +222,13 @@ func TestPixelOutsidePaletteRange(t *testing.T) { b.WriteString(headerStr) b.WriteString(paletteStr) - // Image descriptor: 2x1, no local palette. + // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") // Encode the pixels; some pvals trigger the expected error. - pix := []byte{pval, pval} - enc := &bytes.Buffer{} - w := lzw.NewWriter(enc, lzw.LSB, 2) - if _, err := w.Write(pix); err != nil { - t.Fatalf("Write: %v", err) - } - if err := w.Close(); err != nil { - t.Fatalf("Close: %v", err) - } - b.WriteByte(byte(len(enc.Bytes()))) - b.Write(enc.Bytes()) + enc := lzwEncode([]byte{pval, pval}) + b.WriteByte(byte(len(enc))) + b.Write(enc) b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteString(trailerStr) @@ -254,6 +242,36 @@ func TestPixelOutsidePaletteRange(t *testing.T) { } } +func TestTransparentPixelOutsidePaletteRange(t *testing.T) { + b := &bytes.Buffer{} + + // Manufacture a GIF with a 2 color palette. + b.WriteString(headerStr) + b.WriteString(paletteStr) + + // Graphic Control Extension: transparency, transparent color index = 3. + // + // This index, 3, is out of range of the global palette and there is no + // local palette in the subsequent image descriptor. This is an error + // according to the spec, but Firefox and Google Chrome seem OK with this. + // + // See golang.org/issue/15059. + b.WriteString("\x21\xf9\x04\x01\x00\x00\x03\x00") + + // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. + b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") + + // Encode the pixels. + enc := lzwEncode([]byte{0x03, 0x03}) + b.WriteByte(byte(len(enc))) + b.Write(enc) + b.WriteByte(0x00) // An empty block signifies the end of the image data. + + b.WriteString(trailerStr) + + try(t, b.Bytes(), "") +} + func TestLoopCount(t *testing.T) { data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" + "\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;") diff --git a/libgo/go/image/internal/imageutil/gen.go b/libgo/go/image/internal/imageutil/gen.go index fc1e707f0fd..6792b28a45b 100644 --- a/libgo/go/image/internal/imageutil/gen.go +++ b/libgo/go/image/internal/imageutil/gen.go @@ -95,32 +95,51 @@ const sratioCase = ` %s // This is an inline version of image/color/ycbcr.go's func YCbCrToRGB. - yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200. + yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200. cb1 := int32(src.Cb[ci]) - 128 cr1 := int32(src.Cr[ci]) - 128 - r := (yy1 + 91881*cr1) >> 16 - g := (yy1 - 22554*cb1 - 46802*cr1) >> 16 - b := (yy1 + 116130*cb1) >> 16 - if r < 0 { - r = 0 - } else if r > 255 { - r = 255 + + // The bit twiddling below is equivalent to + // + // r := (yy1 + 91881*cr1) >> 16 + // if r < 0 { + // r = 0 + // } else if r > 0xff { + // r = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute g and b uses a similar pattern. + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) } - if g < 0 { - g = 0 - } else if g > 255 { - g = 255 + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) } - if b < 0 { - b = 0 - } else if b > 255 { - b = 255 + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) } - dpix[x+0] = uint8(r) - dpix[x+1] = uint8(g) - dpix[x+2] = uint8(b) - dpix[x+3] = 255 + + // use a temp slice to hint to the compiler that a single bounds check suffices + rgba := dpix[x : x+4 : len(dpix)] + rgba[0] = uint8(r) + rgba[1] = uint8(g) + rgba[2] = uint8(b) + rgba[3] = 255 } } ` diff --git a/libgo/go/image/internal/imageutil/impl.go b/libgo/go/image/internal/imageutil/impl.go index fd7826d4a97..3696b08e419 100644 --- a/libgo/go/image/internal/imageutil/impl.go +++ b/libgo/go/image/internal/imageutil/impl.go @@ -44,32 +44,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { // This is an inline version of image/color/ycbcr.go's func YCbCrToRGB. - yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200. + yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200. cb1 := int32(src.Cb[ci]) - 128 cr1 := int32(src.Cr[ci]) - 128 - r := (yy1 + 91881*cr1) >> 16 - g := (yy1 - 22554*cb1 - 46802*cr1) >> 16 - b := (yy1 + 116130*cb1) >> 16 - if r < 0 { - r = 0 - } else if r > 255 { - r = 255 + + // The bit twiddling below is equivalent to + // + // r := (yy1 + 91881*cr1) >> 16 + // if r < 0 { + // r = 0 + // } else if r > 0xff { + // r = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute g and b uses a similar pattern. + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) } - if g < 0 { - g = 0 - } else if g > 255 { - g = 255 + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) } - if b < 0 { - b = 0 - } else if b > 255 { - b = 255 + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) } - dpix[x+0] = uint8(r) - dpix[x+1] = uint8(g) - dpix[x+2] = uint8(b) - dpix[x+3] = 255 + // use a temp slice to hint to the compiler that a single bounds check suffices + rgba := dpix[x : x+4 : len(dpix)] + rgba[0] = uint8(r) + rgba[1] = uint8(g) + rgba[2] = uint8(b) + rgba[3] = 255 } } @@ -83,32 +101,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po ci := ciBase + sx/2 // This is an inline version of image/color/ycbcr.go's func YCbCrToRGB. - yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200. + yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200. cb1 := int32(src.Cb[ci]) - 128 cr1 := int32(src.Cr[ci]) - 128 - r := (yy1 + 91881*cr1) >> 16 - g := (yy1 - 22554*cb1 - 46802*cr1) >> 16 - b := (yy1 + 116130*cb1) >> 16 - if r < 0 { - r = 0 - } else if r > 255 { - r = 255 + + // The bit twiddling below is equivalent to + // + // r := (yy1 + 91881*cr1) >> 16 + // if r < 0 { + // r = 0 + // } else if r > 0xff { + // r = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute g and b uses a similar pattern. + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) } - if g < 0 { - g = 0 - } else if g > 255 { - g = 255 + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) } - if b < 0 { - b = 0 - } else if b > 255 { - b = 255 + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) } - dpix[x+0] = uint8(r) - dpix[x+1] = uint8(g) - dpix[x+2] = uint8(b) - dpix[x+3] = 255 + // use a temp slice to hint to the compiler that a single bounds check suffices + rgba := dpix[x : x+4 : len(dpix)] + rgba[0] = uint8(r) + rgba[1] = uint8(g) + rgba[2] = uint8(b) + rgba[3] = 255 } } @@ -122,32 +158,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po ci := ciBase + sx/2 // This is an inline version of image/color/ycbcr.go's func YCbCrToRGB. - yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200. + yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200. cb1 := int32(src.Cb[ci]) - 128 cr1 := int32(src.Cr[ci]) - 128 - r := (yy1 + 91881*cr1) >> 16 - g := (yy1 - 22554*cb1 - 46802*cr1) >> 16 - b := (yy1 + 116130*cb1) >> 16 - if r < 0 { - r = 0 - } else if r > 255 { - r = 255 + + // The bit twiddling below is equivalent to + // + // r := (yy1 + 91881*cr1) >> 16 + // if r < 0 { + // r = 0 + // } else if r > 0xff { + // r = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute g and b uses a similar pattern. + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) } - if g < 0 { - g = 0 - } else if g > 255 { - g = 255 + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) } - if b < 0 { - b = 0 - } else if b > 255 { - b = 255 + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) } - dpix[x+0] = uint8(r) - dpix[x+1] = uint8(g) - dpix[x+2] = uint8(b) - dpix[x+3] = 255 + // use a temp slice to hint to the compiler that a single bounds check suffices + rgba := dpix[x : x+4 : len(dpix)] + rgba[0] = uint8(r) + rgba[1] = uint8(g) + rgba[2] = uint8(b) + rgba[3] = 255 } } @@ -160,32 +214,50 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { // This is an inline version of image/color/ycbcr.go's func YCbCrToRGB. - yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200. + yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200. cb1 := int32(src.Cb[ci]) - 128 cr1 := int32(src.Cr[ci]) - 128 - r := (yy1 + 91881*cr1) >> 16 - g := (yy1 - 22554*cb1 - 46802*cr1) >> 16 - b := (yy1 + 116130*cb1) >> 16 - if r < 0 { - r = 0 - } else if r > 255 { - r = 255 + + // The bit twiddling below is equivalent to + // + // r := (yy1 + 91881*cr1) >> 16 + // if r < 0 { + // r = 0 + // } else if r > 0xff { + // r = ^int32(0) + // } + // + // but uses fewer branches and is faster. + // Note that the uint8 type conversion in the return + // statement will convert ^int32(0) to 0xff. + // The code below to compute g and b uses a similar pattern. + r := yy1 + 91881*cr1 + if uint32(r)&0xff000000 == 0 { + r >>= 16 + } else { + r = ^(r >> 31) } - if g < 0 { - g = 0 - } else if g > 255 { - g = 255 + + g := yy1 - 22554*cb1 - 46802*cr1 + if uint32(g)&0xff000000 == 0 { + g >>= 16 + } else { + g = ^(g >> 31) } - if b < 0 { - b = 0 - } else if b > 255 { - b = 255 + + b := yy1 + 116130*cb1 + if uint32(b)&0xff000000 == 0 { + b >>= 16 + } else { + b = ^(b >> 31) } - dpix[x+0] = uint8(r) - dpix[x+1] = uint8(g) - dpix[x+2] = uint8(b) - dpix[x+3] = 255 + // use a temp slice to hint to the compiler that a single bounds check suffices + rgba := dpix[x : x+4 : len(dpix)] + rgba[0] = uint8(r) + rgba[1] = uint8(g) + rgba[2] = uint8(b) + rgba[3] = 255 } } diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go index adf97abbd1d..c5834219a3e 100644 --- a/libgo/go/image/jpeg/reader.go +++ b/libgo/go/image/jpeg/reader.go @@ -641,6 +641,12 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { return nil, err } } + + if d.progressive { + if err := d.reconstructProgressiveImage(); err != nil { + return nil, err + } + } if d.img1 != nil { return d.img1, nil } diff --git a/libgo/go/image/jpeg/scan.go b/libgo/go/image/jpeg/scan.go index 99734c01af0..e1104d27c23 100644 --- a/libgo/go/image/jpeg/scan.go +++ b/libgo/go/image/jpeg/scan.go @@ -173,7 +173,6 @@ func (d *decoder) processSOS(n int) error { compIndex := scan[i].compIndex hi := d.comp[compIndex].h vi := d.comp[compIndex].v - qt := &d.quant[d.comp[compIndex].tq] for j := 0; j < hi*vi; 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. @@ -286,55 +285,19 @@ func (d *decoder) processSOS(n int) error { } if d.progressive { - if zigEnd != blockSize-1 || al != 0 { - // We haven't completely decoded this 8x8 block. Save the coefficients. - d.progCoeffs[compIndex][by*mxx*hi+bx] = 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] + // Save the coefficients. + d.progCoeffs[compIndex][by*mxx*hi+bx] = b + // At this point, we could call reconstructBlock 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. Instead, reconstructBlock is called on each + // accumulated block by the reconstructProgressiveImage method after all of the + // SOS markers are processed. + continue } - idct(&b) - dst, stride := []byte(nil), 0 - if d.nComp == 1 { - dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride - } else { - switch compIndex { - case 0: - dst, stride = d.img3.Y[8*(by*d.img3.YStride+bx):], d.img3.YStride - case 1: - dst, stride = d.img3.Cb[8*(by*d.img3.CStride+bx):], d.img3.CStride - case 2: - dst, stride = d.img3.Cr[8*(by*d.img3.CStride+bx):], d.img3.CStride - case 3: - dst, stride = d.blackPix[8*(by*d.blackStride+bx):], d.blackStride - default: - return UnsupportedError("too many components") - } - } - // 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) - } + if err := d.reconstructBlock(&b, bx, by, int(compIndex)); err != nil { + return err } } // for j } // for i @@ -470,3 +433,70 @@ func (d *decoder) refineNonZeroes(b *block, zig, zigEnd, nz, delta int32) (int32 } return zig, nil } + +func (d *decoder) reconstructProgressiveImage() error { + // The h0, mxx, by and bx variables have the same meaning as in the + // processSOS method. + h0 := d.comp[0].h + mxx := (d.width + 8*h0 - 1) / (8 * h0) + for i := 0; i < d.nComp; i++ { + if d.progCoeffs[i] == nil { + continue + } + v := 8 * d.comp[0].v / d.comp[i].v + h := 8 * d.comp[0].h / d.comp[i].h + stride := mxx * d.comp[i].h + for by := 0; by*v < d.height; by++ { + for bx := 0; bx*h < d.width; bx++ { + if err := d.reconstructBlock(&d.progCoeffs[i][by*stride+bx], bx, by, i); err != nil { + return err + } + } + } + } + return nil +} + +// reconstructBlock dequantizes, performs the inverse DCT and stores the block +// to the image. +func (d *decoder) reconstructBlock(b *block, bx, by, compIndex int) error { + qt := &d.quant[d.comp[compIndex].tq] + for zig := 0; zig < blockSize; zig++ { + b[unzig[zig]] *= qt[zig] + } + idct(b) + dst, stride := []byte(nil), 0 + if d.nComp == 1 { + dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride + } else { + switch compIndex { + case 0: + dst, stride = d.img3.Y[8*(by*d.img3.YStride+bx):], d.img3.YStride + case 1: + dst, stride = d.img3.Cb[8*(by*d.img3.CStride+bx):], d.img3.CStride + case 2: + dst, stride = d.img3.Cr[8*(by*d.img3.CStride+bx):], d.img3.CStride + case 3: + dst, stride = d.blackPix[8*(by*d.blackStride+bx):], d.blackStride + default: + return UnsupportedError("too many components") + } + } + // 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) + } + } + return nil +} diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go index 9e6f985f7e2..2dd5ed80736 100644 --- a/libgo/go/image/png/reader.go +++ b/libgo/go/image/png/reader.go @@ -717,6 +717,13 @@ func (d *decoder) parseChunk() error { case "IDAT": if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.stage == dsSeenIHDR && cbPaletted(d.cb)) { return chunkOrderError + } else if d.stage == dsSeenIDAT { + // Ignore trailing zero-length or garbage IDAT chunks. + // + // This does not affect valid PNG images that contain multiple IDAT + // chunks, since the first call to parseIDAT below will consume all + // consecutive IDAT chunks required for decoding the image. + break } d.stage = dsSeenIDAT return d.parseIDAT(length) diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go index f058f6b2275..0bc4203acbe 100644 --- a/libgo/go/image/png/reader_test.go +++ b/libgo/go/image/png/reader_test.go @@ -350,6 +350,33 @@ func TestIncompleteIDATOnRowBoundary(t *testing.T) { } } +func TestTrailingIDATChunks(t *testing.T) { + // The following is a valid 1x1 PNG image containing color.Gray{255} and + // a trailing zero-length IDAT chunk (see PNG specification section 12.9): + const ( + ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x00\x00\x00\x00\x3a\x7e\x9b\x55" + idatWhite = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\xfa\x0f\x08\x00\x00\xff\xff\x01\x05\x01\x02\x5a\xdd\x39\xcd" + idatZero = "\x00\x00\x00\x00IDAT\x35\xaf\x06\x1e" + iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" + ) + _, err := Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatZero + iend)) + if err != nil { + t.Fatalf("decoding valid image: %v", err) + } + + // Non-zero-length trailing IDAT chunks should be ignored (recoverable error). + // The following chunk contains a single pixel with color.Gray{0}. + const idatBlack = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" + + img, err := Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatBlack + iend)) + if err != nil { + t.Fatalf("trailing IDAT not ignored: %v", err) + } + if img.At(0, 0) == (color.Gray{0}) { + t.Fatal("decoded image from trailing IDAT chunk") + } +} + func TestMultipletRNSChunks(t *testing.T) { /* The following is a valid 1x1 paletted PNG image with a 1-element palette diff --git a/libgo/go/image/testdata/video-001.progressive.truncated.jpeg b/libgo/go/image/testdata/video-001.progressive.truncated.jpeg Binary files differnew file mode 100644 index 00000000000..b5be8bc7639 --- /dev/null +++ b/libgo/go/image/testdata/video-001.progressive.truncated.jpeg diff --git a/libgo/go/image/testdata/video-001.progressive.truncated.png b/libgo/go/image/testdata/video-001.progressive.truncated.png Binary files differnew file mode 100644 index 00000000000..baf19812265 --- /dev/null +++ b/libgo/go/image/testdata/video-001.progressive.truncated.png |