diff options
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/image')
42 files changed, 2369 insertions, 435 deletions
diff --git a/llgo/third_party/gofrontend/libgo/go/image/color/color.go b/llgo/third_party/gofrontend/libgo/go/image/color/color.go index ff596a76a36..cae059b6daa 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/color/color.go +++ b/llgo/third_party/gofrontend/libgo/go/image/color/color.go @@ -9,14 +9,20 @@ package color // The conversion may be lossy. type Color interface { // RGBA returns the alpha-premultiplied red, green, blue and alpha values - // for the color. Each value ranges within [0, 0xFFFF], but is represented - // by a uint32 so that multiplying by a blend factor up to 0xFFFF will not + // for the color. Each value ranges within [0, 0xffff], but is represented + // by a uint32 so that multiplying by a blend factor up to 0xffff will not // overflow. + // + // An alpha-premultiplied color component c has been scaled by alpha (a), + // so has valid values 0 <= c <= a. RGBA() (r, g, b, a uint32) } -// RGBA represents a traditional 32-bit alpha-premultiplied color, -// having 8 bits for each of red, green, blue and alpha. +// RGBA represents a traditional 32-bit alpha-premultiplied color, having 8 +// bits for each of red, green, blue and alpha. +// +// An alpha-premultiplied color component C has been scaled by alpha (A), so +// has valid values 0 <= C <= A. type RGBA struct { R, G, B, A uint8 } @@ -33,8 +39,11 @@ func (c RGBA) RGBA() (r, g, b, a uint32) { return } -// RGBA64 represents a 64-bit alpha-premultiplied color, -// having 16 bits for each of red, green, blue and alpha. +// RGBA64 represents a 64-bit alpha-premultiplied color, having 16 bits for +// each of red, green, blue and alpha. +// +// An alpha-premultiplied color component C has been scaled by alpha (A), so +// has valid values 0 <= C <= A. type RGBA64 struct { R, G, B, A uint16 } @@ -262,32 +271,39 @@ func (p Palette) Convert(c Color) Color { } // Index returns the index of the palette color closest to c in Euclidean -// R,G,B space. +// R,G,B,A space. func (p Palette) Index(c Color) int { // A batch version of this computation is in image/draw/draw.go. - cr, cg, cb, _ := c.RGBA() - ret, bestSSD := 0, uint32(1<<32-1) + cr, cg, cb, ca := c.RGBA() + ret, bestSum := 0, uint32(1<<32-1) for i, v := range p { - vr, vg, vb, _ := v.RGBA() - // We shift by 1 bit to avoid potential uint32 overflow in - // sum-squared-difference. - delta := (int32(cr) - int32(vr)) >> 1 - ssd := uint32(delta * delta) - delta = (int32(cg) - int32(vg)) >> 1 - ssd += uint32(delta * delta) - delta = (int32(cb) - int32(vb)) >> 1 - ssd += uint32(delta * delta) - if ssd < bestSSD { - if ssd == 0 { + vr, vg, vb, va := v.RGBA() + sum := sqDiff(cr, vr) + sqDiff(cg, vg) + sqDiff(cb, vb) + sqDiff(ca, va) + if sum < bestSum { + if sum == 0 { return i } - ret, bestSSD = i, ssd + ret, bestSum = i, sum } } return ret } +// sqDiff returns the squared-difference of x and y, shifted by 2 so that +// adding four of those won't overflow a uint32. +// +// x and y are both assumed to be in the range [0, 0xffff]. +func sqDiff(x, y uint32) uint32 { + var d uint32 + if x > y { + d = x - y + } else { + d = y - x + } + return (d * d) >> 2 +} + // Standard colors. var ( Black = Gray16{0} diff --git a/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr.go b/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr.go index 4c2f29ea021..4bcb07dce22 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr.go +++ b/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr.go @@ -11,26 +11,27 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) { // Cb = -0.1687*R - 0.3313*G + 0.5000*B + 128 // Cr = 0.5000*R - 0.4187*G - 0.0813*B + 128 // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. - r1 := int(r) - g1 := int(g) - b1 := int(b) + + r1 := int32(r) + g1 := int32(g) + b1 := int32(b) 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 > 255 { - yy = 255 + } else if yy > 0xff { + yy = 0xff } if cb < 0 { cb = 0 - } else if cb > 255 { - cb = 255 + } else if cb > 0xff { + cb = 0xff } if cr < 0 { cr = 0 - } else if cr > 255 { - cr = 255 + } else if cr > 0xff { + cr = 0xff } return uint8(yy), uint8(cb), uint8(cr) } @@ -42,26 +43,27 @@ func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) { // G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128) // B = Y' + 1.77200*(Cb-128) // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. - yy1 := int(y)<<16 + 1<<15 - cb1 := int(cb) - 128 - cr1 := int(cr) - 128 + + yy1 := int32(y) * 0x10100 // 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 > 255 { - r = 255 + } else if r > 0xff { + r = 0xff } if g < 0 { g = 0 - } else if g > 255 { - g = 255 + } else if g > 0xff { + g = 0xff } if b < 0 { b = 0 - } else if b > 255 { - b = 255 + } else if b > 0xff { + b = 0xff } return uint8(r), uint8(g), uint8(b) } @@ -82,8 +84,45 @@ type YCbCr struct { } func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) { - r, g, b := YCbCrToRGB(c.Y, c.Cb, c.Cr) - return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff + // This code is a copy of the YCbCrToRGB function above, except that it + // returns values in the range [0, 0xffff] instead of [0, 0xff]. There is a + // subtle difference between doing this and having YCbCr satisfy the Color + // interface by first converting to an RGBA. The latter loses some + // information by going to and from 8 bits per channel. + // + // For example, this code: + // const y, cb, cr = 0x7f, 0x7f, 0x7f + // r, g, b := color.YCbCrToRGB(y, cb, cr) + // r0, g0, b0, _ := color.YCbCr{y, cb, cr}.RGBA() + // r1, g1, b1, _ := color.RGBA{r, g, b, 0xff}.RGBA() + // fmt.Printf("0x%04x 0x%04x 0x%04x\n", r0, g0, b0) + // fmt.Printf("0x%04x 0x%04x 0x%04x\n", r1, g1, b1) + // prints: + // 0x7e18 0x808e 0x7db9 + // 0x7e7e 0x8080 0x7d7d + + yy1 := int32(c.Y) * 0x10100 // Convert 0x12 to 0x121200. + cb1 := int32(c.Cb) - 128 + cr1 := int32(c.Cr) - 128 + r := (yy1 + 91881*cr1) >> 8 + g := (yy1 - 22554*cb1 - 46802*cr1) >> 8 + b := (yy1 + 116130*cb1) >> 8 + if r < 0 { + r = 0 + } else if r > 0xffff { + r = 0xffff + } + if g < 0 { + g = 0 + } else if g > 0xffff { + g = 0xffff + } + if b < 0 { + b = 0 + } else if b > 0xffff { + b = 0xffff + } + return uint32(r), uint32(g), uint32(b), 0xffff } // YCbCrModel is the Model for Y'CbCr colors. @@ -97,3 +136,64 @@ func yCbCrModel(c Color) Color { y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) return YCbCr{y, u, v} } + +// RGBToCMYK converts an RGB triple to a CMYK quadruple. +func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) { + rr := uint32(r) + gg := uint32(g) + bb := uint32(b) + w := rr + if w < gg { + w = gg + } + if w < bb { + w = bb + } + if w == 0 { + return 0, 0, 0, 0xff + } + c := (w - rr) * 0xff / w + m := (w - gg) * 0xff / w + y := (w - bb) * 0xff / w + return uint8(c), uint8(m), uint8(y), uint8(0xff - w) +} + +// 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 + return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8) +} + +// CMYK represents a fully opaque CMYK color, having 8 bits for each of cyan, +// magenta, yellow and black. +// +// It is not associated with any particular color profile. +type CMYK struct { + C, M, Y, K uint8 +} + +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 +} + +// CMYKModel is the Model for CMYK colors. +var CMYKModel Model = ModelFunc(cmykModel) + +func cmykModel(c Color) Color { + if _, ok := c.(CMYK); ok { + return c + } + r, g, b, _ := c.RGBA() + cc, mm, yy, kk := RGBToCMYK(uint8(r>>8), uint8(g>>8), uint8(b>>8)) + return CMYK{cc, mm, yy, kk} +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr_test.go b/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr_test.go index 92a0e6ff1e7..5da49d379aa 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/color/ycbcr_test.go @@ -5,6 +5,7 @@ package color import ( + "fmt" "testing" ) @@ -15,19 +16,134 @@ func delta(x, y uint8) uint8 { return y - x } -// Test that a subset of RGB space can be converted to YCbCr and back to within -// 1/256 tolerance. -func TestRoundtrip(t *testing.T) { - for r := 0; r < 255; r += 7 { - for g := 0; g < 255; g += 5 { - for b := 0; b < 255; b += 3 { +func eq(c0, c1 Color) error { + r0, g0, b0, a0 := c0.RGBA() + r1, g1, b1, a1 := c1.RGBA() + if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 { + return fmt.Errorf("got 0x%04x 0x%04x 0x%04x 0x%04x\nwant 0x%04x 0x%04x 0x%04x 0x%04x", + r0, g0, b0, a0, r1, g1, b1, a1) + } + return nil +} + +// TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr +// and back to within 2/256 tolerance. +func TestYCbCrRoundtrip(t *testing.T) { + for r := 0; r < 256; r += 7 { + for g := 0; g < 256; g += 5 { + for b := 0; b < 256; b += 3 { r0, g0, b0 := uint8(r), uint8(g), uint8(b) y, cb, cr := RGBToYCbCr(r0, g0, b0) r1, g1, b1 := YCbCrToRGB(y, cb, cr) + if delta(r0, r1) > 2 || delta(g0, g1) > 2 || delta(b0, b1) > 2 { + t.Fatalf("\nr0, g0, b0 = %d, %d, %d\ny, cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d", + r0, g0, b0, y, cb, cr, r1, g1, b1) + } + } + } + } +} + +// TestYCbCrToRGBConsistency tests that calling the RGBA method (16 bit color) +// then truncating to 8 bits is equivalent to calling the YCbCrToRGB function (8 +// bit color). +func TestYCbCrToRGBConsistency(t *testing.T) { + for y := 0; y < 256; y += 7 { + for cb := 0; cb < 256; cb += 5 { + for cr := 0; cr < 256; cr += 3 { + x := YCbCr{uint8(y), uint8(cb), uint8(cr)} + r0, g0, b0, _ := x.RGBA() + r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8) + r2, g2, b2 := YCbCrToRGB(x.Y, x.Cb, x.Cr) + if r1 != r2 || g1 != g2 || b1 != b2 { + t.Fatalf("y, cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d", + y, cb, cr, r1, g1, b1, r2, g2, b2) + } + } + } + } +} + +// TestYCbCrGray tests that YCbCr colors are a superset of Gray colors. +func TestYCbCrGray(t *testing.T) { + for i := 0; i < 256; i++ { + if err := eq(YCbCr{uint8(i), 0x80, 0x80}, Gray{uint8(i)}); err != nil { + t.Errorf("i=0x%02x:\n%v", i, err) + } + } +} + +// TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK +// and back to within 1/256 tolerance. +func TestCMYKRoundtrip(t *testing.T) { + for r := 0; r < 256; r += 7 { + for g := 0; g < 256; g += 5 { + for b := 0; b < 256; b += 3 { + r0, g0, b0 := uint8(r), uint8(g), uint8(b) + c, m, y, k := RGBToCMYK(r0, g0, b0) + r1, g1, b1 := CMYKToRGB(c, m, y, k) if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 { - t.Fatalf("r0, g0, b0 = %d, %d, %d r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1) + t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nc, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d", + r0, g0, b0, c, m, y, k, r1, g1, b1) } } } } } + +// TestCMYKToRGBConsistency tests that calling the RGBA method (16 bit color) +// then truncating to 8 bits is equivalent to calling the CMYKToRGB function (8 +// bit color). +func TestCMYKToRGBConsistency(t *testing.T) { + for c := 0; c < 256; c += 7 { + for m := 0; m < 256; m += 5 { + for y := 0; y < 256; y += 3 { + for k := 0; k < 256; k += 11 { + x := CMYK{uint8(c), uint8(m), uint8(y), uint8(k)} + r0, g0, b0, _ := x.RGBA() + r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8) + r2, g2, b2 := CMYKToRGB(x.C, x.M, x.Y, x.K) + if r1 != r2 || g1 != g2 || b1 != b2 { + t.Fatalf("c, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d", + c, m, y, k, r1, g1, b1, r2, g2, b2) + } + } + } + } + } +} + +// TestCMYKGray tests that CMYK colors are a superset of Gray colors. +func TestCMYKGray(t *testing.T) { + for i := 0; i < 256; i++ { + if err := eq(CMYK{0x00, 0x00, 0x00, uint8(255 - i)}, Gray{uint8(i)}); err != nil { + t.Errorf("i=0x%02x:\n%v", i, err) + } + } +} + +func TestPalette(t *testing.T) { + p := Palette{ + RGBA{0xff, 0xff, 0xff, 0xff}, + RGBA{0x80, 0x00, 0x00, 0xff}, + RGBA{0x7f, 0x00, 0x00, 0x7f}, + RGBA{0x00, 0x00, 0x00, 0x7f}, + RGBA{0x00, 0x00, 0x00, 0x00}, + RGBA{0x40, 0x40, 0x40, 0x40}, + } + // Check that, for a Palette with no repeated colors, the closest color to + // each element is itself. + for i, c := range p { + j := p.Index(c) + if i != j { + t.Errorf("Index(%v): got %d (color = %v), want %d", c, j, p[j], i) + } + } + // Check that finding the closest color considers alpha, not just red, + // green and blue. + got := p.Convert(RGBA{0x80, 0x00, 0x00, 0x80}) + want := RGBA{0x7f, 0x00, 0x00, 0x7f} + if got != want { + t.Errorf("got %v, want %v", got, want) + } +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/decode_example_test.go b/llgo/third_party/gofrontend/libgo/go/image/decode_example_test.go index 21e90fea4f8..81fa0378e17 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/decode_example_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/decode_example_test.go @@ -61,22 +61,22 @@ func Example() { } // Output: // bin red green blue alpha - // 0x0000-0x0fff: 353 759 7228 0 - // 0x1000-0x1fff: 629 2944 1036 0 - // 0x2000-0x2fff: 1075 2319 984 0 - // 0x3000-0x3fff: 838 2291 988 0 - // 0x4000-0x4fff: 540 1302 542 0 - // 0x5000-0x5fff: 319 971 263 0 - // 0x6000-0x6fff: 316 377 178 0 - // 0x7000-0x7fff: 581 280 216 0 - // 0x8000-0x8fff: 3457 228 274 0 - // 0x9000-0x9fff: 2294 237 334 0 - // 0xa000-0xafff: 938 283 370 0 - // 0xb000-0xbfff: 322 338 401 0 - // 0xc000-0xcfff: 229 386 295 0 - // 0xd000-0xdfff: 263 416 281 0 - // 0xe000-0xefff: 538 433 312 0 - // 0xf000-0xffff: 2758 1886 1748 15450 + // 0x0000-0x0fff: 364 790 7242 0 + // 0x1000-0x1fff: 645 2967 1039 0 + // 0x2000-0x2fff: 1072 2299 979 0 + // 0x3000-0x3fff: 820 2266 980 0 + // 0x4000-0x4fff: 537 1305 541 0 + // 0x5000-0x5fff: 319 962 261 0 + // 0x6000-0x6fff: 322 375 177 0 + // 0x7000-0x7fff: 601 279 214 0 + // 0x8000-0x8fff: 3478 227 273 0 + // 0x9000-0x9fff: 2260 234 329 0 + // 0xa000-0xafff: 921 282 373 0 + // 0xb000-0xbfff: 321 335 397 0 + // 0xc000-0xcfff: 229 388 298 0 + // 0xd000-0xdfff: 260 414 277 0 + // 0xe000-0xefff: 516 428 298 0 + // 0xf000-0xffff: 2785 1899 1772 15450 } const data = ` diff --git a/llgo/third_party/gofrontend/libgo/go/image/decode_test.go b/llgo/third_party/gofrontend/libgo/go/image/decode_test.go index 8dee57ee467..d16ef8a1a4d 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/decode_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/decode_test.go @@ -6,6 +6,7 @@ package image_test import ( "bufio" + "fmt" "image" "image/color" "os" @@ -32,6 +33,9 @@ var imageTests = []imageTest{ // 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}, + {"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}, // 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}, @@ -74,6 +78,11 @@ func withinTolerance(c0, c1 color.Color, tolerance int) bool { } func TestDecode(t *testing.T) { + rgba := func(c color.Color) string { + r, g, b, a := c.RGBA() + return fmt.Sprintf("rgba = 0x%04x, 0x%04x, 0x%04x, 0x%04x for %T%v", r, g, b, a, c, c) + } + golden := make(map[string]image.Image) loop: for _, it := range imageTests { @@ -94,13 +103,14 @@ loop: } b := g.Bounds() if !b.Eq(m.Bounds()) { - t.Errorf("%s: want bounds %v got %v", it.filename, b, m.Bounds()) + t.Errorf("%s: got bounds %v want %v", it.filename, m.Bounds(), b) continue loop } for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { if !withinTolerance(g.At(x, y), m.At(x, y), it.tolerance) { - t.Errorf("%s: at (%d, %d), want %v got %v", it.filename, x, y, g.At(x, y), m.At(x, y)) + t.Errorf("%s: at (%d, %d):\ngot %v\nwant %v", + it.filename, x, y, rgba(m.At(x, y)), rgba(g.At(x, y))) continue loop } } diff --git a/llgo/third_party/gofrontend/libgo/go/image/draw/bench_test.go b/llgo/third_party/gofrontend/libgo/go/image/draw/bench_test.go index cc62e25f1bb..7b89f95d118 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/draw/bench_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/draw/bench_test.go @@ -7,6 +7,7 @@ package draw import ( "image" "image/color" + "reflect" "testing" ) @@ -15,6 +16,11 @@ const ( srcw, srch = 400, 300 ) +var palette = color.Palette{ + color.Black, + color.White, +} + // bench benchmarks drawing src and mask images onto a dst image with the // given op and the color models to create those images from. // The created images' pixels are initialized to non-zero values. @@ -50,13 +56,48 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) { } dst = dst1 default: - b.Fatal("unknown destination color model", dcm) + // The == operator isn't defined on a color.Palette (a slice), so we + // use reflection. + if reflect.DeepEqual(dcm, palette) { + dst1 := image.NewPaletted(image.Rect(0, 0, dstw, dsth), palette) + for y := 0; y < dsth; y++ { + for x := 0; x < dstw; x++ { + dst1.SetColorIndex(x, y, uint8(x^y)&1) + } + } + dst = dst1 + } else { + b.Fatal("unknown destination color model", dcm) + } } var src image.Image switch scm { case nil: src = &image.Uniform{C: color.RGBA{0x11, 0x22, 0x33, 0xff}} + case color.CMYKModel: + src1 := image.NewCMYK(image.Rect(0, 0, srcw, srch)) + for y := 0; y < srch; y++ { + for x := 0; x < srcw; x++ { + src1.SetCMYK(x, y, color.CMYK{ + uint8(13 * x % 0x100), + uint8(11 * y % 0x100), + uint8((11*x + 13*y) % 0x100), + uint8((31*x + 37*y) % 0x100), + }) + } + } + src = src1 + case color.GrayModel: + src1 := image.NewGray(image.Rect(0, 0, srcw, srch)) + for y := 0; y < srch; y++ { + for x := 0; x < srcw; x++ { + src1.SetGray(x, y, color.Gray{ + uint8((11*x + 13*y) % 0x100), + }) + } + } + src = src1 case color.RGBAModel: src1 := image.NewRGBA(image.Rect(0, 0, srcw, srch)) for y := 0; y < srch; y++ { @@ -179,6 +220,14 @@ func BenchmarkYCbCr(b *testing.B) { bench(b, color.RGBAModel, color.YCbCrModel, nil, Over) } +func BenchmarkGray(b *testing.B) { + bench(b, color.RGBAModel, color.GrayModel, nil, Over) +} + +func BenchmarkCMYK(b *testing.B) { + bench(b, color.RGBAModel, color.CMYKModel, nil, Over) +} + func BenchmarkGlyphOver(b *testing.B) { bench(b, color.RGBAModel, nil, color.AlphaModel, Over) } @@ -187,6 +236,10 @@ func BenchmarkRGBA(b *testing.B) { bench(b, color.RGBAModel, color.RGBA64Model, nil, Src) } +func BenchmarkPaletted(b *testing.B) { + bench(b, palette, color.RGBAModel, nil, Src) +} + // The BenchmarkGenericFoo functions exercise the generic, slow-path code. func BenchmarkGenericOver(b *testing.B) { diff --git a/llgo/third_party/gofrontend/libgo/go/image/draw/clip_test.go b/llgo/third_party/gofrontend/libgo/go/image/draw/clip_test.go index 65381f72f65..0abf53e5c78 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/draw/clip_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/draw/clip_test.go @@ -139,7 +139,19 @@ var clipTests = []clipTest{ image.Pt(20, 0), image.Pt(20, 0), }, - // TODO(nigeltao): write more tests. + { + "clip sr and mr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.Rect(23, 23, 55, 86), + image.Rect(44, 44, 87, 58), + image.Pt(10, 10), + image.Pt(11, 11), + false, + image.Rect(33, 33, 45, 47), + image.Pt(43, 43), + image.Pt(44, 44), + }, } func TestClip(t *testing.T) { @@ -149,12 +161,12 @@ func TestClip(t *testing.T) { for _, c := range clipTests { dst := dst0.SubImage(c.dr).(*image.RGBA) src := src0.SubImage(c.sr).(*image.RGBA) - var mask image.Image - if !c.nilMask { - mask = mask0.SubImage(c.mr) - } r, sp, mp := c.r, c.sp, c.mp - clip(dst, &r, src, &sp, mask, &mp) + if c.nilMask { + clip(dst, &r, src, &sp, nil, nil) + } else { + clip(dst, &r, src, &sp, mask0.SubImage(c.mr), &mp) + } // Check that the actual results equal the expected results. if !c.r0.Eq(r) { @@ -173,17 +185,17 @@ func TestClip(t *testing.T) { } // Check that the clipped rectangle is contained by the dst / src / mask - // rectangles, in their respective co-ordinate spaces. + // rectangles, in their respective coordinate spaces. if !r.In(c.dr) { t.Errorf("%s: c.dr %v does not contain r %v", c.desc, c.dr, r) } - // sr is r translated into src's co-ordinate space. + // sr is r translated into src's coordinate space. sr := r.Add(c.sp.Sub(c.dr.Min)) if !sr.In(c.sr) { t.Errorf("%s: c.sr %v does not contain sr %v", c.desc, c.sr, sr) } if !c.nilMask { - // mr is r translated into mask's co-ordinate space. + // mr is r translated into mask's coordinate space. mr := r.Add(c.mp.Sub(c.dr.Min)) if !mr.In(c.mr) { t.Errorf("%s: c.mr %v does not contain mr %v", c.desc, c.mr, mr) diff --git a/llgo/third_party/gofrontend/libgo/go/image/draw/draw.go b/llgo/third_party/gofrontend/libgo/go/image/draw/draw.go index 661230e7c59..9419d5e72a7 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/draw/draw.go +++ b/llgo/third_party/gofrontend/libgo/go/image/draw/draw.go @@ -5,12 +5,13 @@ // Package draw provides image composition functions. // // See "The Go image/draw package" for an introduction to this package: -// http://golang.org/doc/articles/image_draw.html +// https://golang.org/doc/articles/image_draw.html package draw import ( "image" "image/color" + "image/internal/imageutil" ) // m is the maximum color value returned by image.Color.RGBA. @@ -67,7 +68,7 @@ func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp ima } // clip clips r against each image's bounds (after translating into the -// destination image's co-ordinate space) and shifts the points sp and mp by +// destination image's coordinate space) and shifts the points sp and mp by // the same amount as the change in r.Min. func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) { orig := r.Min @@ -81,10 +82,12 @@ func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask if dx == 0 && dy == 0 { return } - (*sp).X += dx - (*sp).Y += dy - (*mp).X += dx - (*mp).Y += dy + sp.X += dx + sp.Y += dy + if mp != nil { + mp.X += dx + mp.Y += dy + } } func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool { @@ -122,9 +125,19 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas drawNRGBAOver(dst0, r, src0, sp) return case *image.YCbCr: - if drawYCbCr(dst0, r, src0, sp) { + // An image.YCbCr is always fully opaque, and so if the + // mask is nil (i.e. fully opaque) then the op is + // effectively always Src. Similarly for image.Gray and + // image.CMYK. + if imageutil.DrawYCbCr(dst0, r, src0, sp) { return } + case *image.Gray: + drawGray(dst0, r, src0, sp) + return + case *image.CMYK: + drawCMYK(dst0, r, src0, sp) + return } } else if mask0, ok := mask.(*image.Alpha); ok { switch src0 := src.(type) { @@ -146,9 +159,15 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas drawNRGBASrc(dst0, r, src0, sp) return case *image.YCbCr: - if drawYCbCr(dst0, r, src0, sp) { + if imageutil.DrawYCbCr(dst0, r, src0, sp) { return } + case *image.Gray: + drawGray(dst0, r, src0, sp) + return + case *image.CMYK: + drawCMYK(dst0, r, src0, sp) + return } } } @@ -157,6 +176,7 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas case *image.Paletted: if op == Src && mask == nil && !processBackward(dst, r, src, sp) { drawPaletted(dst0, r, src, sp, false) + return } } @@ -237,16 +257,20 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { sr, sg, sb, sa := src.RGBA() + sr8 := uint8(sr >> 8) + sg8 := uint8(sg >> 8) + sb8 := uint8(sb >> 8) + sa8 := uint8(sa >> 8) // The built-in copy function is faster than a straightforward for loop to fill the destination with // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and // then use the first row as the slice source for the remaining rows. i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 for i := i0; i < i1; i += 4 { - dst.Pix[i+0] = uint8(sr >> 8) - dst.Pix[i+1] = uint8(sg >> 8) - dst.Pix[i+2] = uint8(sb >> 8) - dst.Pix[i+3] = uint8(sa >> 8) + dst.Pix[i+0] = sr8 + dst.Pix[i+1] = sg8 + dst.Pix[i+2] = sb8 + dst.Pix[i+3] = sa8 } firstRow := dst.Pix[i0:i1] for y := r.Min.Y + 1; y < r.Max.Y; y++ { @@ -313,9 +337,11 @@ func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.P ddelta = dst.Stride sdelta = src.Stride } else { - // If the source start point is higher than the destination start point, then we compose the rows - // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to - // check the x co-ordinates because the built-in copy function can handle overlapping slices. + // If the source start point is higher than the destination start + // point, then we compose the rows in bottom-up order instead of + // top-down. Unlike the drawCopyOver function, we don't have to check + // the x coordinates because the built-in copy function can handle + // overlapping slices. d0 += (dy - 1) * dst.Stride s0 += (dy - 1) * src.Stride ddelta = -dst.Stride @@ -390,72 +416,46 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image } } -func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) { - // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil - // (i.e. fully opaque) then the op is effectively always Src. - x0 := (r.Min.X - dst.Rect.Min.X) * 4 - x1 := (r.Max.X - dst.Rect.Min.X) * 4 - y0 := r.Min.Y - dst.Rect.Min.Y - y1 := r.Max.Y - dst.Rect.Min.Y - switch src.SubsampleRatio { - case image.YCbCrSubsampleRatio444: - for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { - dpix := dst.Pix[y*dst.Stride:] - yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) - ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) - for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { - rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) - dpix[x+0] = rr - dpix[x+1] = gg - dpix[x+2] = bb - dpix[x+3] = 255 - } - } - case image.YCbCrSubsampleRatio422: - for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { - dpix := dst.Pix[y*dst.Stride:] - yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) - ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2 - for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { - ci := ciBase + sx/2 - rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) - dpix[x+0] = rr - dpix[x+1] = gg - dpix[x+2] = bb - dpix[x+3] = 255 - } - } - case image.YCbCrSubsampleRatio420: - for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { - dpix := dst.Pix[y*dst.Stride:] - yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) - ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2 - for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { - ci := ciBase + sx/2 - rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) - dpix[x+0] = rr - dpix[x+1] = gg - dpix[x+2] = bb - dpix[x+3] = 255 - } +func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point) { + i0 := (r.Min.X - dst.Rect.Min.X) * 4 + i1 := (r.Max.X - dst.Rect.Min.X) * 4 + si0 := (sp.X - src.Rect.Min.X) * 1 + yMax := r.Max.Y - dst.Rect.Min.Y + + y := r.Min.Y - dst.Rect.Min.Y + sy := sp.Y - src.Rect.Min.Y + for ; y != yMax; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + spix := src.Pix[sy*src.Stride:] + + for i, si := i0, si0; i < i1; i, si = i+4, si+1 { + p := spix[si] + dpix[i+0] = p + dpix[i+1] = p + dpix[i+2] = p + dpix[i+3] = 255 } - case image.YCbCrSubsampleRatio440: - for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { - dpix := dst.Pix[y*dst.Stride:] - yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) - ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X) - for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { - rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) - dpix[x+0] = rr - dpix[x+1] = gg - dpix[x+2] = bb - dpix[x+3] = 255 - } + } +} + +func drawCMYK(dst *image.RGBA, r image.Rectangle, src *image.CMYK, sp image.Point) { + i0 := (r.Min.X - dst.Rect.Min.X) * 4 + i1 := (r.Max.X - dst.Rect.Min.X) * 4 + si0 := (sp.X - src.Rect.Min.X) * 4 + yMax := r.Max.Y - dst.Rect.Min.Y + + y := r.Min.Y - dst.Rect.Min.Y + sy := sp.Y - src.Rect.Min.Y + for ; y != yMax; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + spix := src.Pix[sy*src.Stride:] + + for i, si := i0, si0; i < i1; i, si = i+4, si+4 { + dpix[i+0], dpix[i+1], dpix[i+2] = + color.CMYKToRGB(spix[si+0], spix[si+1], spix[si+2], spix[si+3]) + dpix[i+3] = 255 } - default: - return false } - return true } func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { @@ -555,6 +555,20 @@ func clamp(i int32) int32 { return i } +// sqDiff returns the squared-difference of x and y, shifted by 2 so that +// adding four of those won't overflow a uint32. +// +// x and y are both assumed to be in the range [0, 0xffff]. +func sqDiff(x, y int32) uint32 { + var d uint32 + if x > y { + d = uint32(x - y) + } else { + d = uint32(y - x) + } + return (d * d) >> 2 +} + func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) { // TODO(nigeltao): handle the case where the dst and src overlap. // Does it even make sense to try and do Floyd-Steinberg whilst @@ -564,14 +578,15 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, // dst.At. The dst.Set equivalent is a batch version of the algorithm // used by color.Palette's Index method in image/color/color.go, plus // optional Floyd-Steinberg error diffusion. - palette, pix, stride := [][3]int32(nil), []byte(nil), 0 + palette, pix, stride := [][4]int32(nil), []byte(nil), 0 if p, ok := dst.(*image.Paletted); ok { - palette = make([][3]int32, len(p.Palette)) + palette = make([][4]int32, len(p.Palette)) for i, col := range p.Palette { - r, g, b, _ := col.RGBA() + r, g, b, a := col.RGBA() palette[i][0] = int32(r) palette[i][1] = int32(g) palette[i][2] = int32(b) + palette[i][3] = int32(a) } pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride } @@ -579,10 +594,10 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, // quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization // errors that have been propagated to the pixels in the current and next // rows. The +2 simplifies calculation near the edges. - var quantErrorCurr, quantErrorNext [][3]int32 + var quantErrorCurr, quantErrorNext [][4]int32 if floydSteinberg { - quantErrorCurr = make([][3]int32, r.Dx()+2) - quantErrorNext = make([][3]int32, r.Dx()+2) + quantErrorCurr = make([][4]int32, r.Dx()+2) + quantErrorNext = make([][4]int32, r.Dx()+2) } // Loop over each source pixel. @@ -591,30 +606,25 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, for x := 0; x != r.Dx(); x++ { // er, eg and eb are the pixel's R,G,B values plus the // optional Floyd-Steinberg error. - sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() - er, eg, eb := int32(sr), int32(sg), int32(sb) + sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA() + er, eg, eb, ea := int32(sr), int32(sg), int32(sb), int32(sa) if floydSteinberg { er = clamp(er + quantErrorCurr[x+1][0]/16) eg = clamp(eg + quantErrorCurr[x+1][1]/16) eb = clamp(eb + quantErrorCurr[x+1][2]/16) + ea = clamp(ea + quantErrorCurr[x+1][3]/16) } if palette != nil { - // Find the closest palette color in Euclidean R,G,B space: the - // one that minimizes sum-squared-difference. We shift by 1 bit - // to avoid potential uint32 overflow in sum-squared-difference. + // Find the closest palette color in Euclidean R,G,B,A space: + // the one that minimizes sum-squared-difference. // TODO(nigeltao): consider smarter algorithms. - bestIndex, bestSSD := 0, uint32(1<<32-1) + bestIndex, bestSum := 0, uint32(1<<32-1) for index, p := range palette { - delta := (er - p[0]) >> 1 - ssd := uint32(delta * delta) - delta = (eg - p[1]) >> 1 - ssd += uint32(delta * delta) - delta = (eb - p[2]) >> 1 - ssd += uint32(delta * delta) - if ssd < bestSSD { - bestIndex, bestSSD = index, ssd - if ssd == 0 { + sum := sqDiff(er, p[0]) + sqDiff(eg, p[1]) + sqDiff(eb, p[2]) + sqDiff(ea, p[3]) + if sum < bestSum { + bestIndex, bestSum = index, sum + if sum == 0 { break } } @@ -627,11 +637,13 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, er -= int32(palette[bestIndex][0]) eg -= int32(palette[bestIndex][1]) eb -= int32(palette[bestIndex][2]) + ea -= int32(palette[bestIndex][3]) } else { out.R = uint16(er) out.G = uint16(eg) out.B = uint16(eb) + out.A = uint16(ea) // The third argument is &out instead of out (and out is // declared outside of the inner loop) to avoid the implicit // conversion to color.Color here allocating memory in the @@ -641,32 +653,37 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, if !floydSteinberg { continue } - sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() + sr, sg, sb, sa = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() er -= int32(sr) eg -= int32(sg) eb -= int32(sb) + ea -= int32(sa) } // Propagate the Floyd-Steinberg quantization error. quantErrorNext[x+0][0] += er * 3 quantErrorNext[x+0][1] += eg * 3 quantErrorNext[x+0][2] += eb * 3 + quantErrorNext[x+0][3] += ea * 3 quantErrorNext[x+1][0] += er * 5 quantErrorNext[x+1][1] += eg * 5 quantErrorNext[x+1][2] += eb * 5 + quantErrorNext[x+1][3] += ea * 5 quantErrorNext[x+2][0] += er * 1 quantErrorNext[x+2][1] += eg * 1 quantErrorNext[x+2][2] += eb * 1 + quantErrorNext[x+2][3] += ea * 1 quantErrorCurr[x+2][0] += er * 7 quantErrorCurr[x+2][1] += eg * 7 quantErrorCurr[x+2][2] += eb * 7 + quantErrorCurr[x+2][3] += ea * 7 } // Recycle the quantization error buffers. if floydSteinberg { quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr for i := range quantErrorNext { - quantErrorNext[i] = [3]int32{} + quantErrorNext[i] = [4]int32{} } } } diff --git a/llgo/third_party/gofrontend/libgo/go/image/draw/draw_test.go b/llgo/third_party/gofrontend/libgo/go/image/draw/draw_test.go index 0dd7fbd479a..a58f0f49849 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/draw/draw_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/draw/draw_test.go @@ -74,6 +74,26 @@ func vgradCr() image.Image { return m } +func vgradGray() image.Image { + m := image.NewGray(image.Rect(0, 0, 16, 16)) + for y := 0; y < 16; y++ { + for x := 0; x < 16; x++ { + m.Set(x, y, color.Gray{uint8(y * 0x11)}) + } + } + return m +} + +func vgradMagenta() image.Image { + m := image.NewCMYK(image.Rect(0, 0, 16, 16)) + for y := 0; y < 16; y++ { + for x := 0; x < 16; x++ { + m.Set(x, y, color.CMYK{0, uint8(y * 0x11), 0, 0x3f}) + } + } + return m +} + func hgradRed(alpha int) Image { m := image.NewRGBA(image.Rect(0, 0, 16, 16)) for y := 0; y < 16; y++ { @@ -147,6 +167,26 @@ var drawTests = []drawTest{ {"ycbcrAlphaSrc", vgradCr(), fillAlpha(192), Src, color.RGBA{8, 28, 0, 192}}, {"ycbcrNil", vgradCr(), nil, Over, color.RGBA{11, 38, 0, 255}}, {"ycbcrNilSrc", vgradCr(), nil, Src, color.RGBA{11, 38, 0, 255}}, + // Uniform mask (100%, 75%, nil) and variable Gray source. + // At (x, y) == (8, 8): + // The destination pixel is {136, 0, 0, 255}. + // The source pixel is {136} in Gray-space, which is {136, 136, 136, 255} in RGBA-space. + {"gray", vgradGray(), fillAlpha(255), Over, color.RGBA{136, 136, 136, 255}}, + {"graySrc", vgradGray(), fillAlpha(255), Src, color.RGBA{136, 136, 136, 255}}, + {"grayAlpha", vgradGray(), fillAlpha(192), Over, color.RGBA{136, 102, 102, 255}}, + {"grayAlphaSrc", vgradGray(), fillAlpha(192), Src, color.RGBA{102, 102, 102, 192}}, + {"grayNil", vgradGray(), nil, Over, color.RGBA{136, 136, 136, 255}}, + {"grayNilSrc", vgradGray(), nil, Src, color.RGBA{136, 136, 136, 255}}, + // Uniform mask (100%, 75%, nil) and variable CMYK source. + // At (x, y) == (8, 8): + // The destination pixel is {136, 0, 0, 255}. + // The source pixel is {0, 136, 0, 63} in CMYK-space, which is {192, 89, 192} in RGB-space. + {"cmyk", vgradMagenta(), fillAlpha(255), Over, color.RGBA{192, 89, 192, 255}}, + {"cmykSrc", vgradMagenta(), fillAlpha(255), Src, color.RGBA{192, 89, 192, 255}}, + {"cmykAlpha", vgradMagenta(), fillAlpha(192), Over, color.RGBA{178, 67, 145, 255}}, + {"cmykAlphaSrc", vgradMagenta(), fillAlpha(192), Src, color.RGBA{145, 67, 145, 192}}, + {"cmykNil", vgradMagenta(), nil, Over, color.RGBA{192, 89, 192, 255}}, + {"cmykNilSrc", vgradMagenta(), nil, Src, color.RGBA{192, 89, 192, 255}}, // Variable mask and variable source. // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. diff --git a/llgo/third_party/gofrontend/libgo/go/image/geom.go b/llgo/third_party/gofrontend/libgo/go/image/geom.go index 6ebaf67da84..e1cd4dc1e3e 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/geom.go +++ b/llgo/third_party/gofrontend/libgo/go/image/geom.go @@ -5,6 +5,7 @@ package image import ( + "image/color" "strconv" ) @@ -62,7 +63,7 @@ func (p Point) Mod(r Rectangle) Point { // Eq reports whether p and q are equal. func (p Point) Eq(q Point) bool { - return p.X == q.X && p.Y == q.Y + return p == q } // ZP is the zero Point. @@ -77,6 +78,10 @@ func Pt(X, Y int) Point { // It is well-formed if Min.X <= Max.X and likewise for Y. Points are always // well-formed. A rectangle's methods always return well-formed outputs for // well-formed inputs. +// +// A Rectangle is also an Image whose bounds are the rectangle itself. At +// returns color.Opaque for points in the rectangle and color.Transparent +// otherwise. type Rectangle struct { Min, Max Point } @@ -164,6 +169,12 @@ func (r Rectangle) Intersect(s Rectangle) Rectangle { // Union returns the smallest rectangle that contains both r and s. func (r Rectangle) Union(s Rectangle) Rectangle { + if r.Empty() { + return s + } + if s.Empty() { + return r + } if r.Min.X > s.Min.X { r.Min.X = s.Min.X } @@ -184,15 +195,16 @@ func (r Rectangle) Empty() bool { return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y } -// Eq reports whether r and s are equal. +// Eq reports whether r and s contain the same set of points. All empty +// rectangles are considered equal. func (r Rectangle) Eq(s Rectangle) bool { - return r.Min.X == s.Min.X && r.Min.Y == s.Min.Y && - r.Max.X == s.Max.X && r.Max.Y == s.Max.Y + return r == s || r.Empty() && s.Empty() } // Overlaps reports whether r and s have a non-empty intersection. func (r Rectangle) Overlaps(s Rectangle) bool { - return r.Min.X < s.Max.X && s.Min.X < r.Max.X && + return !r.Empty() && !s.Empty() && + r.Min.X < s.Max.X && s.Min.X < r.Max.X && r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y } @@ -219,10 +231,30 @@ func (r Rectangle) Canon() Rectangle { return r } +// At implements the Image interface. +func (r Rectangle) At(x, y int) color.Color { + if (Point{x, y}).In(r) { + return color.Opaque + } + return color.Transparent +} + +// Bounds implements the Image interface. +func (r Rectangle) Bounds() Rectangle { + return r +} + +// ColorModel implements the Image interface. +func (r Rectangle) ColorModel() color.Model { + return color.Alpha16Model +} + // ZR is the zero Rectangle. var ZR Rectangle -// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}. +// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}. The returned +// rectangle has minimum and maximum coordinates swapped if necessary so that +// it is well-formed. func Rect(x0, y0, x1, y1 int) Rectangle { if x0 > x1 { x0, x1 = x1, x0 diff --git a/llgo/third_party/gofrontend/libgo/go/image/geom_test.go b/llgo/third_party/gofrontend/libgo/go/image/geom_test.go new file mode 100644 index 00000000000..6e9c6a13c2c --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/geom_test.go @@ -0,0 +1,115 @@ +// Copyright 2015 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 image + +import ( + "fmt" + "testing" +) + +func TestRectangle(t *testing.T) { + // in checks that every point in f is in g. + in := func(f, g Rectangle) error { + if !f.In(g) { + return fmt.Errorf("f=%s, f.In(%s): got false, want true", f, g) + } + for y := f.Min.Y; y < f.Max.Y; y++ { + for x := f.Min.X; x < f.Max.X; x++ { + p := Point{x, y} + if !p.In(g) { + return fmt.Errorf("p=%s, p.In(%s): got false, want true", p, g) + } + } + } + return nil + } + + rects := []Rectangle{ + Rect(0, 0, 10, 10), + Rect(1, 2, 3, 4), + Rect(4, 6, 10, 10), + Rect(2, 3, 12, 5), + Rect(-1, -2, 0, 0), + Rect(-1, -2, 4, 6), + Rect(-10, -20, 30, 40), + Rect(8, 8, 8, 8), + Rect(88, 88, 88, 88), + Rect(6, 5, 4, 3), + } + + // r.Eq(s) should be equivalent to every point in r being in s, and every + // point in s being in r. + for _, r := range rects { + for _, s := range rects { + got := r.Eq(s) + want := in(r, s) == nil && in(s, r) == nil + if got != want { + t.Errorf("Eq: r=%s, s=%s: got %t, want %t", r, s, got, want) + } + } + } + + // The intersection should be the largest rectangle a such that every point + // in a is both in r and in s. + for _, r := range rects { + for _, s := range rects { + a := r.Intersect(s) + if err := in(a, r); err != nil { + t.Errorf("Intersect: r=%s, s=%s, a=%s, a not in r: %v", r, s, a, err) + } + if err := in(a, s); err != nil { + t.Errorf("Intersect: r=%s, s=%s, a=%s, a not in s: %v", r, s, a, err) + } + if a.Empty() == r.Overlaps(s) { + t.Errorf("Intersect: r=%s, s=%s, a=%s: empty=%t same as overlaps=%t", + r, s, a, a.Empty(), r.Overlaps(s)) + } + largerThanA := [4]Rectangle{a, a, a, a} + largerThanA[0].Min.X-- + largerThanA[1].Min.Y-- + largerThanA[2].Max.X++ + largerThanA[3].Max.Y++ + for i, b := range largerThanA { + if b.Empty() { + // b isn't actually larger than a. + continue + } + if in(b, r) == nil && in(b, s) == nil { + t.Errorf("Intersect: r=%s, s=%s, a=%s, b=%s, i=%d: intersection could be larger", + r, s, a, b, i) + } + } + } + } + + // The union should be the smallest rectangle a such that every point in r + // is in a and every point in s is in a. + for _, r := range rects { + for _, s := range rects { + a := r.Union(s) + if err := in(r, a); err != nil { + t.Errorf("Union: r=%s, s=%s, a=%s, r not in a: %v", r, s, a, err) + } + if err := in(s, a); err != nil { + t.Errorf("Union: r=%s, s=%s, a=%s, s not in a: %v", r, s, a, err) + } + if a.Empty() { + // You can't get any smaller than a. + continue + } + smallerThanA := [4]Rectangle{a, a, a, a} + smallerThanA[0].Min.X++ + smallerThanA[1].Min.Y++ + smallerThanA[2].Max.X-- + smallerThanA[3].Max.Y-- + for i, b := range smallerThanA { + if in(r, b) == nil && in(s, b) == nil { + t.Errorf("Union: r=%s, s=%s, a=%s, b=%s, i=%d: union could be smaller", + r, s, a, b, i) + } + } + } + } +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/gif/reader.go b/llgo/third_party/gofrontend/libgo/go/image/gif/reader.go index 5a863e204f3..6a133124ad5 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/gif/reader.go +++ b/llgo/third_party/gofrontend/libgo/go/image/gif/reader.go @@ -32,15 +32,20 @@ type reader interface { // Masks etc. const ( // Fields. - fColorMapFollows = 1 << 7 - - // Image fields. - ifLocalColorTable = 1 << 7 - ifInterlace = 1 << 6 - ifPixelSizeMask = 7 + fColorTable = 1 << 7 + fInterlace = 1 << 6 + fColorTableBitsMask = 7 // Graphic control flags. gcTransparentColorSet = 1 << 0 + gcDisposalMethodMask = 7 << 2 +) + +// Disposal Methods. +const ( + DisposalNone = 0x01 + DisposalBackground = 0x02 + DisposalPrevious = 0x03 ) // Section indicators. @@ -66,14 +71,10 @@ type decoder struct { vers string width int height int - flags byte - headerFields byte - backgroundIndex byte loopCount int delayTime int - - // Unused from header. - aspect byte + backgroundIndex byte + disposalMethod byte // From image descriptor. imageFields byte @@ -83,13 +84,13 @@ type decoder struct { hasTransparentIndex bool // Computed. - pixelSize uint - globalColorMap color.Palette + globalColorTable color.Palette // Used when decoding. - delay []int - image []*image.Paletted - tmp [1024]byte // must be at least 768 so we can read color map + delay []int + disposal []byte + image []*image.Paletted + tmp [1024]byte // must be at least 768 so we can read color table } // blockReader parses the block structure of GIF image data, which @@ -122,7 +123,7 @@ func (b *blockReader) Read(p []byte) (int, error) { b.err = io.EOF return 0, b.err } - b.slice = b.tmp[0:blockLen] + b.slice = b.tmp[:blockLen] if _, b.err = io.ReadFull(b.r, b.slice); b.err != nil { return 0, b.err } @@ -149,12 +150,6 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { return nil } - if d.headerFields&fColorMapFollows != 0 { - if d.globalColorMap, err = d.readColorMap(); err != nil { - return err - } - } - for { c, err := d.r.ReadByte() if err != nil { @@ -171,19 +166,22 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { if err != nil { return err } - useLocalColorMap := d.imageFields&fColorMapFollows != 0 - if useLocalColorMap { - m.Palette, err = d.readColorMap() + useLocalColorTable := d.imageFields&fColorTable != 0 + if useLocalColorTable { + m.Palette, err = d.readColorTable(d.imageFields) if err != nil { return err } } else { - m.Palette = d.globalColorMap + if d.globalColorTable == nil { + return errors.New("gif: no color table") + } + m.Palette = d.globalColorTable } if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) { - if !useLocalColorMap { - // Clone the global color map. - m.Palette = append(color.Palette(nil), d.globalColorMap...) + if !useLocalColorTable { + // Clone the global color table. + m.Palette = append(color.Palette(nil), d.globalColorTable...) } m.Palette[d.transparentIndex] = color.RGBA{} } @@ -204,9 +202,18 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { } return errNotEnough } - // Both lzwr and br should be exhausted. Reading from them - // should yield (0, io.EOF). - if n, err := lzwr.Read(d.tmp[:1]); n != 0 || err != io.EOF { + // Both lzwr and br should be exhausted. Reading from them should + // yield (0, io.EOF). + // + // The spec (Appendix F - Compression), says that "An End of + // Information code... must be the last code output by the encoder + // 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 + // 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) { if err != nil { return err } @@ -229,12 +236,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { } // Undo the interlacing if necessary. - if d.imageFields&ifInterlace != 0 { + if d.imageFields&fInterlace != 0 { uninterlace(m) } d.image = append(d.image, m) d.delay = append(d.delay, d.delayTime) + d.disposal = append(d.disposal, d.disposalMethod) // The GIF89a spec, Section 23 (Graphic Control Extension) says: // "The scope of this extension is the first graphic rendering block // to follow." We therefore reset the GCE fields to zero. @@ -254,44 +262,39 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { } func (d *decoder) readHeaderAndScreenDescriptor() error { - _, err := io.ReadFull(d.r, d.tmp[0:13]) + _, err := io.ReadFull(d.r, d.tmp[:13]) if err != nil { return err } - d.vers = string(d.tmp[0:6]) + d.vers = string(d.tmp[:6]) if d.vers != "GIF87a" && d.vers != "GIF89a" { return fmt.Errorf("gif: can't recognize format %s", d.vers) } d.width = int(d.tmp[6]) + int(d.tmp[7])<<8 d.height = int(d.tmp[8]) + int(d.tmp[9])<<8 - d.headerFields = d.tmp[10] - d.backgroundIndex = d.tmp[11] - d.aspect = d.tmp[12] - d.loopCount = -1 - d.pixelSize = uint(d.headerFields&7) + 1 + if fields := d.tmp[10]; fields&fColorTable != 0 { + d.backgroundIndex = d.tmp[11] + // readColorTable overwrites the contents of d.tmp, but that's OK. + if d.globalColorTable, err = d.readColorTable(fields); err != nil { + return err + } + } + // d.tmp[12] is the Pixel Aspect Ratio, which is ignored. return nil } -func (d *decoder) readColorMap() (color.Palette, error) { - if d.pixelSize > 8 { - return nil, fmt.Errorf("gif: can't handle %d bits per pixel", d.pixelSize) - } - numColors := 1 << d.pixelSize - if d.imageFields&ifLocalColorTable != 0 { - numColors = 1 << ((d.imageFields & ifPixelSizeMask) + 1) - } - numValues := 3 * numColors - _, err := io.ReadFull(d.r, d.tmp[0:numValues]) +func (d *decoder) readColorTable(fields byte) (color.Palette, error) { + n := 1 << (1 + uint(fields&fColorTableBitsMask)) + _, err := io.ReadFull(d.r, d.tmp[:3*n]) if err != nil { - return nil, fmt.Errorf("gif: short read on color map: %s", err) + return nil, fmt.Errorf("gif: short read on color table: %s", err) } - colorMap := make(color.Palette, numColors) - j := 0 - for i := range colorMap { - colorMap[i] = color.RGBA{d.tmp[j+0], d.tmp[j+1], d.tmp[j+2], 0xFF} + j, p := 0, make(color.Palette, n) + for i := range p { + p[i] = color.RGBA{d.tmp[j+0], d.tmp[j+1], d.tmp[j+2], 0xFF} j += 3 } - return colorMap, nil + return p, nil } func (d *decoder) readExtension() error { @@ -318,7 +321,7 @@ func (d *decoder) readExtension() error { return fmt.Errorf("gif: unknown extension 0x%.2x", extension) } if size > 0 { - if _, err := io.ReadFull(d.r, d.tmp[0:size]); err != nil { + if _, err := io.ReadFull(d.r, d.tmp[:size]); err != nil { return err } } @@ -343,12 +346,13 @@ func (d *decoder) readExtension() error { } func (d *decoder) readGraphicControl() error { - if _, err := io.ReadFull(d.r, d.tmp[0:6]); err != nil { + if _, err := io.ReadFull(d.r, d.tmp[:6]); err != nil { return fmt.Errorf("gif: can't read graphic control: %s", err) } - d.flags = d.tmp[1] + flags := d.tmp[1] + d.disposalMethod = (flags & gcDisposalMethodMask) >> 2 d.delayTime = int(d.tmp[2]) | int(d.tmp[3])<<8 - if d.flags&gcTransparentColorSet != 0 { + if flags&gcTransparentColorSet != 0 { d.transparentIndex = d.tmp[4] d.hasTransparentIndex = true } @@ -356,7 +360,7 @@ func (d *decoder) readGraphicControl() error { } func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) { - if _, err := io.ReadFull(d.r, d.tmp[0:9]); err != nil { + if _, err := io.ReadFull(d.r, d.tmp[:9]); err != nil { return nil, fmt.Errorf("gif: can't read image descriptor: %s", err) } left := int(d.tmp[0]) + int(d.tmp[1])<<8 @@ -380,7 +384,7 @@ func (d *decoder) readBlock() (int, error) { if n == 0 || err != nil { return 0, err } - return io.ReadFull(d.r, d.tmp[0:n]) + return io.ReadFull(d.r, d.tmp[:n]) } // interlaceScan defines the ordering for a pass of the interlace algorithm. @@ -429,6 +433,24 @@ type GIF struct { Image []*image.Paletted // The successive images. Delay []int // The successive delay times, one per frame, in 100ths of a second. LoopCount int // The loop count. + // Disposal is the successive disposal methods, one per frame. For + // backwards compatibility, a nil Disposal is valid to pass to EncodeAll, + // and implies that each frame's disposal method is 0 (no disposal + // specified). + Disposal []byte + // Config is the global color table (palette), width and height. A nil or + // empty-color.Palette Config.ColorModel means that each frame has its own + // color table and there is no global color table. Each frame's bounds must + // be within the rectangle defined by the two points (0, 0) and + // (Config.Width, Config.Height). + // + // For backwards compatibility, a zero-valued Config is valid to pass to + // EncodeAll, and implies that the overall GIF's width and height equals + // the first frame's bounds' Rectangle.Max point. + Config image.Config + // BackgroundIndex is the background index in the global color table, for + // use with the DisposalBackground disposal method. + BackgroundIndex byte } // DecodeAll reads a GIF image from r and returns the sequential frames @@ -442,6 +464,13 @@ func DecodeAll(r io.Reader) (*GIF, error) { Image: d.image, LoopCount: d.loopCount, Delay: d.delay, + Disposal: d.disposal, + Config: image.Config{ + ColorModel: d.globalColorTable, + Width: d.width, + Height: d.height, + }, + BackgroundIndex: d.backgroundIndex, } return gif, nil } @@ -454,7 +483,7 @@ func DecodeConfig(r io.Reader) (image.Config, error) { return image.Config{}, err } return image.Config{ - ColorModel: d.globalColorMap, + ColorModel: d.globalColorTable, Width: d.width, Height: d.height, }, nil diff --git a/llgo/third_party/gofrontend/libgo/go/image/gif/reader_test.go b/llgo/third_party/gofrontend/libgo/go/image/gif/reader_test.go index 7b6f504367c..c294195b6f7 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/gif/reader_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/gif/reader_test.go @@ -17,8 +17,8 @@ import ( const ( headerStr = "GIF89a" + "\x02\x00\x01\x00" + // width=2, height=1 - "\x80\x00\x00" // headerFields=(a color map of 2 pixels), backgroundIndex, aspect - paletteStr = "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette + "\x80\x00\x00" // headerFields=(a color table of 2 pixels), backgroundIndex, aspect + paletteStr = "\x10\x20\x30\x40\x50\x60" // the color table, also known as a palette trailerStr = "\x3b" ) @@ -141,7 +141,7 @@ var testGIF = []byte{ 'G', 'I', 'F', '8', '9', 'a', 1, 0, 1, 0, // w=1, h=1 (6) 128, 0, 0, // headerFields, bg, aspect (10) - 0, 0, 0, 1, 1, 1, // color map and graphics control (13) + 0, 0, 0, 1, 1, 1, // color table and graphics control (13) 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00, // (19) // frame 1 (0,0 - 1,1) 0x2c, @@ -200,22 +200,26 @@ func TestNoPalette(t *testing.T) { 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, 128} + pix := []byte{0, 3} enc := &bytes.Buffer{} w := lzw.NewWriter(enc, lzw.LSB, 2) - w.Write(pix) - w.Close() + 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()) b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteString(trailerStr) - try(t, b.Bytes(), "gif: invalid pixel value") + try(t, b.Bytes(), "gif: no color table") } func TestPixelOutsidePaletteRange(t *testing.T) { - for _, pval := range []byte{0, 1, 2, 3, 255} { + for _, pval := range []byte{0, 1, 2, 3} { b := &bytes.Buffer{} // Manufacture a GIF with a 2 color palette. @@ -229,8 +233,12 @@ func TestPixelOutsidePaletteRange(t *testing.T) { pix := []byte{pval, pval} enc := &bytes.Buffer{} w := lzw.NewWriter(enc, lzw.LSB, 2) - w.Write(pix) - w.Close() + 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()) b.WriteByte(0x00) // An empty block signifies the end of the image data. @@ -245,3 +253,24 @@ func TestPixelOutsidePaletteRange(t *testing.T) { try(t, b.Bytes(), want) } } + +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;") + img, err := DecodeAll(bytes.NewReader(data)) + if err != nil { + t.Fatal("DecodeAll:", err) + } + w := new(bytes.Buffer) + err = EncodeAll(w, img) + if err != nil { + t.Fatal("EncodeAll:", err) + } + img1, err := DecodeAll(w) + if err != nil { + t.Fatal("DecodeAll:", err) + } + if img.LoopCount != img1.LoopCount { + t.Errorf("loop count mismatch: %d vs %d", img.LoopCount, img1.LoopCount) + } +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/gif/writer.go b/llgo/third_party/gofrontend/libgo/go/image/gif/writer.go index 49abde704c8..dd317901d48 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/gif/writer.go +++ b/llgo/third_party/gofrontend/libgo/go/image/gif/writer.go @@ -6,6 +6,7 @@ package gif import ( "bufio" + "bytes" "compress/lzw" "errors" "image" @@ -52,9 +53,13 @@ type encoder struct { w writer err error // g is a reference to the data that is being encoded. - g *GIF - // buf is a scratch buffer. It must be at least 768 so we can write the color map. - buf [1024]byte + g GIF + // globalCT is the size in bytes of the global color table. + globalCT int + // buf is a scratch buffer. It must be at least 256 for the blockWriter. + buf [256]byte + globalColorTable [3 * 256]byte + localColorTable [3 * 256]byte } // blockWriter writes the block structure of GIF image data, which @@ -116,18 +121,27 @@ func (e *encoder) writeHeader() { return } - pm := e.g.Image[0] // Logical screen width and height. - writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx())) - writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy())) + writeUint16(e.buf[0:2], uint16(e.g.Config.Width)) + writeUint16(e.buf[2:4], uint16(e.g.Config.Height)) e.write(e.buf[:4]) - // All frames have a local color table, so a global color table - // is not needed. - e.buf[0] = 0x00 - e.buf[1] = 0x00 // Background Color Index. - e.buf[2] = 0x00 // Pixel Aspect Ratio. - e.write(e.buf[:3]) + if p, ok := e.g.Config.ColorModel.(color.Palette); ok && len(p) > 0 { + paddedSize := log2(len(p)) // Size of Global Color Table: 2^(1+n). + e.buf[0] = fColorTable | uint8(paddedSize) + e.buf[1] = e.g.BackgroundIndex + e.buf[2] = 0x00 // Pixel Aspect Ratio. + e.write(e.buf[:3]) + e.globalCT = encodeColorTable(e.globalColorTable[:], p, paddedSize) + e.write(e.globalColorTable[:e.globalCT]) + } else { + // All frames have a local color table, so a global color table + // is not needed. + e.buf[0] = 0x00 + e.buf[1] = 0x00 // Background Color Index. + e.buf[2] = 0x00 // Pixel Aspect Ratio. + e.write(e.buf[:3]) + } // Add animation info if necessary. if len(e.g.Image) > 1 { @@ -147,28 +161,25 @@ func (e *encoder) writeHeader() { } } -func (e *encoder) writeColorTable(p color.Palette, size int) { - if e.err != nil { - return - } - - for i := 0; i < log2Lookup[size]; i++ { +func encodeColorTable(dst []byte, p color.Palette, size int) int { + n := log2Lookup[size] + for i := 0; i < n; i++ { if i < len(p) { r, g, b, _ := p[i].RGBA() - e.buf[3*i+0] = uint8(r >> 8) - e.buf[3*i+1] = uint8(g >> 8) - e.buf[3*i+2] = uint8(b >> 8) + dst[3*i+0] = uint8(r >> 8) + dst[3*i+1] = uint8(g >> 8) + dst[3*i+2] = uint8(b >> 8) } else { // Pad with black. - e.buf[3*i+0] = 0x00 - e.buf[3*i+1] = 0x00 - e.buf[3*i+2] = 0x00 + dst[3*i+0] = 0x00 + dst[3*i+1] = 0x00 + dst[3*i+2] = 0x00 } } - e.write(e.buf[:3*log2Lookup[size]]) + return 3 * n } -func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { +func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) { if e.err != nil { return } @@ -179,10 +190,14 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { } b := pm.Bounds() - if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X < 0 || b.Min.X >= 1<<16 || b.Min.Y < 0 || b.Min.Y >= 1<<16 { + if b.Min.X < 0 || b.Max.X >= 1<<16 || b.Min.Y < 0 || b.Max.Y >= 1<<16 { e.err = errors.New("gif: image block is too large to encode") return } + if !b.In(image.Rectangle{Max: image.Point{e.g.Config.Width, e.g.Config.Height}}) { + e.err = errors.New("gif: image block is out of bounds") + return + } transparentIndex := -1 for i, c := range pm.Palette { @@ -192,14 +207,14 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { } } - if delay > 0 || transparentIndex != -1 { + if delay > 0 || disposal != 0 || transparentIndex != -1 { e.buf[0] = sExtension // Extension Introducer. e.buf[1] = gcLabel // Graphic Control Label. e.buf[2] = gcBlockSize // Block Size. if transparentIndex != -1 { - e.buf[3] = 0x01 + e.buf[3] = 0x01 | disposal<<2 } else { - e.buf[3] = 0x00 + e.buf[3] = 0x00 | disposal<<2 } writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second) @@ -220,11 +235,15 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { e.write(e.buf[:9]) paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n). - // Interlacing is not supported. - e.writeByte(0x80 | uint8(paddedSize)) - - // Local Color Table. - e.writeColorTable(pm.Palette, paddedSize) + ct := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize) + if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) { + // Use a local color table. + e.writeByte(fColorTable | uint8(paddedSize)) + e.write(e.localColorTable[:ct]) + } else { + // Use the global color table. + e.writeByte(0) + } litWidth := paddedSize + 1 if litWidth < 2 { @@ -281,7 +300,23 @@ func EncodeAll(w io.Writer, g *GIF) error { g.LoopCount = 0 } - e := encoder{g: g} + e := encoder{g: *g} + // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added + // in Go 1.5. Valid Go 1.4 code, such as when the Disposal field is omitted + // in a GIF struct literal, should still produce valid GIFs. + if e.g.Disposal != nil && len(e.g.Image) != len(e.g.Disposal) { + return errors.New("gif: mismatched image and disposal lengths") + } + if e.g.Config == (image.Config{}) { + p := g.Image[0].Bounds().Max + e.g.Config.Width = p.X + e.g.Config.Height = p.Y + } else if e.g.Config.ColorModel != nil { + if _, ok := e.g.Config.ColorModel.(color.Palette); !ok { + return errors.New("gif: GIF color model must be a color.Palette") + } + } + if ww, ok := w.(writer); ok { e.w = ww } else { @@ -290,7 +325,11 @@ func EncodeAll(w io.Writer, g *GIF) error { e.writeHeader() for i, pm := range g.Image { - e.writeImageBlock(pm, g.Delay[i]) + disposal := uint8(0) + if g.Disposal != nil { + disposal = g.Disposal[i] + } + e.writeImageBlock(pm, g.Delay[i], disposal) } e.writeByte(sTrailer) e.flush() @@ -326,8 +365,22 @@ func Encode(w io.Writer, m image.Image, o *Options) error { opts.Drawer.Draw(pm, b, m, image.ZP) } + // When calling Encode instead of EncodeAll, the single-frame image is + // translated such that its top-left corner is (0, 0), so that the single + // frame completely fills the overall GIF's bounds. + if pm.Rect.Min != (image.Point{}) { + dup := *pm + dup.Rect = dup.Rect.Sub(dup.Rect.Min) + pm = &dup + } + return EncodeAll(w, &GIF{ Image: []*image.Paletted{pm}, Delay: []int{0}, + Config: image.Config{ + ColorModel: pm.Palette, + Width: b.Dx(), + Height: b.Dy(), + }, }) } diff --git a/llgo/third_party/gofrontend/libgo/go/image/gif/writer_test.go b/llgo/third_party/gofrontend/libgo/go/image/gif/writer_test.go index 93306ffdb34..db61a5c3c2e 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/gif/writer_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/gif/writer_test.go @@ -8,10 +8,12 @@ import ( "bytes" "image" "image/color" + "image/color/palette" _ "image/png" "io/ioutil" "math/rand" "os" + "reflect" "testing" ) @@ -125,55 +127,317 @@ func TestSubImage(t *testing.T) { } } +// palettesEqual reports whether two color.Palette values are equal, ignoring +// any trailing opaque-black palette entries. +func palettesEqual(p, q color.Palette) bool { + n := len(p) + if n > len(q) { + n = len(q) + } + for i := 0; i < n; i++ { + if p[i] != q[i] { + return false + } + } + for i := n; i < len(p); i++ { + r, g, b, a := p[i].RGBA() + if r != 0 || g != 0 || b != 0 || a != 0xffff { + return false + } + } + for i := n; i < len(q); i++ { + r, g, b, a := q[i].RGBA() + if r != 0 || g != 0 || b != 0 || a != 0xffff { + return false + } + } + return true +} + var frames = []string{ "../testdata/video-001.gif", "../testdata/video-005.gray.gif", } -func TestEncodeAll(t *testing.T) { +func testEncodeAll(t *testing.T, go1Dot5Fields bool, useGlobalColorModel bool) { + const width, height = 150, 103 + g0 := &GIF{ Image: make([]*image.Paletted, len(frames)), Delay: make([]int, len(frames)), LoopCount: 5, } for i, f := range frames { - m, err := readGIF(f) + g, err := readGIF(f) if err != nil { t.Fatal(f, err) } - g0.Image[i] = m.Image[0] + m := g.Image[0] + if m.Bounds().Dx() != width || m.Bounds().Dy() != height { + t.Fatalf("frame %d had unexpected bounds: got %v, want width/height = %d/%d", + i, m.Bounds(), width, height) + } + g0.Image[i] = m } + // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added + // in Go 1.5. Valid Go 1.4 or earlier code should still produce valid GIFs. + // + // On the following line, color.Model is an interface type, and + // color.Palette is a concrete (slice) type. + globalColorModel, backgroundIndex := color.Model(color.Palette(nil)), uint8(0) + if useGlobalColorModel { + globalColorModel, backgroundIndex = color.Palette(palette.WebSafe), uint8(1) + } + if go1Dot5Fields { + g0.Disposal = make([]byte, len(g0.Image)) + for i := range g0.Disposal { + g0.Disposal[i] = DisposalNone + } + g0.Config = image.Config{ + ColorModel: globalColorModel, + Width: width, + Height: height, + } + g0.BackgroundIndex = backgroundIndex + } + var buf bytes.Buffer if err := EncodeAll(&buf, g0); err != nil { t.Fatal("EncodeAll:", err) } - g1, err := DecodeAll(&buf) + encoded := buf.Bytes() + config, err := DecodeConfig(bytes.NewReader(encoded)) + if err != nil { + t.Fatal("DecodeConfig:", err) + } + g1, err := DecodeAll(bytes.NewReader(encoded)) if err != nil { t.Fatal("DecodeAll:", err) } + + if !reflect.DeepEqual(config, g1.Config) { + t.Errorf("DecodeConfig inconsistent with DecodeAll") + } + if !palettesEqual(g1.Config.ColorModel.(color.Palette), globalColorModel.(color.Palette)) { + t.Errorf("unexpected global color model") + } + if w, h := g1.Config.Width, g1.Config.Height; w != width || h != height { + t.Errorf("got config width * height = %d * %d, want %d * %d", w, h, width, height) + } + if g0.LoopCount != g1.LoopCount { t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount) } + if backgroundIndex != g1.BackgroundIndex { + t.Errorf("background indexes differ: %d and %d", backgroundIndex, g1.BackgroundIndex) + } + if len(g0.Image) != len(g1.Image) { + t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image)) + } + if len(g1.Image) != len(g1.Delay) { + t.Fatalf("image and delay lengths differ: %d and %d", len(g1.Image), len(g1.Delay)) + } + if len(g1.Image) != len(g1.Disposal) { + t.Fatalf("image and disposal lengths differ: %d and %d", len(g1.Image), len(g1.Disposal)) + } + for i := range g0.Image { m0, m1 := g0.Image[i], g1.Image[i] if m0.Bounds() != m1.Bounds() { - t.Errorf("%s, bounds differ: %v and %v", frames[i], m0.Bounds(), m1.Bounds()) + t.Errorf("frame %d: bounds differ: %v and %v", i, m0.Bounds(), m1.Bounds()) } d0, d1 := g0.Delay[i], g1.Delay[i] if d0 != d1 { - t.Errorf("%s: delay values differ: %d and %d", frames[i], d0, d1) + t.Errorf("frame %d: delay values differ: %d and %d", i, d0, d1) + } + p0, p1 := uint8(0), g1.Disposal[i] + if go1Dot5Fields { + p0 = DisposalNone + } + if p0 != p1 { + t.Errorf("frame %d: disposal values differ: %d and %d", i, p0, p1) } } +} - g1.Delay = make([]int, 1) - if err := EncodeAll(ioutil.Discard, g1); err == nil { +func TestEncodeAllGo1Dot4(t *testing.T) { testEncodeAll(t, false, false) } +func TestEncodeAllGo1Dot5(t *testing.T) { testEncodeAll(t, true, false) } +func TestEncodeAllGo1Dot5GlobalColorModel(t *testing.T) { testEncodeAll(t, true, true) } + +func TestEncodeMismatchDelay(t *testing.T) { + images := make([]*image.Paletted, 2) + for i := range images { + images[i] = image.NewPaletted(image.Rect(0, 0, 5, 5), palette.Plan9) + } + + g0 := &GIF{ + Image: images, + Delay: make([]int, 1), + } + if err := EncodeAll(ioutil.Discard, g0); err == nil { t.Error("expected error from mismatched delay and image slice lengths") } + + g1 := &GIF{ + Image: images, + Delay: make([]int, len(images)), + Disposal: make([]byte, 1), + } + for i := range g1.Disposal { + g1.Disposal[i] = DisposalNone + } + if err := EncodeAll(ioutil.Discard, g1); err == nil { + t.Error("expected error from mismatched disposal and image slice lengths") + } +} + +func TestEncodeZeroGIF(t *testing.T) { if err := EncodeAll(ioutil.Discard, &GIF{}); err == nil { t.Error("expected error from providing empty gif") } } +func TestEncodeAllFramesOutOfBounds(t *testing.T) { + images := []*image.Paletted{ + image.NewPaletted(image.Rect(0, 0, 5, 5), palette.Plan9), + image.NewPaletted(image.Rect(2, 2, 8, 8), palette.Plan9), + image.NewPaletted(image.Rect(3, 3, 4, 4), palette.Plan9), + } + for _, upperBound := range []int{6, 10} { + g := &GIF{ + Image: images, + Delay: make([]int, len(images)), + Disposal: make([]byte, len(images)), + Config: image.Config{ + Width: upperBound, + Height: upperBound, + }, + } + err := EncodeAll(ioutil.Discard, g) + if upperBound >= 8 { + if err != nil { + t.Errorf("upperBound=%d: %v", upperBound, err) + } + } else { + if err == nil { + t.Errorf("upperBound=%d: got nil error, want non-nil", upperBound) + } + } + } +} + +func TestEncodeNonZeroMinPoint(t *testing.T) { + points := []image.Point{ + image.Point{-8, -9}, + image.Point{-4, -4}, + image.Point{-3, +3}, + image.Point{+0, +0}, + image.Point{+2, +2}, + } + for _, p := range points { + src := image.NewPaletted(image.Rectangle{Min: p, Max: p.Add(image.Point{6, 6})}, palette.Plan9) + var buf bytes.Buffer + if err := Encode(&buf, src, nil); err != nil { + t.Errorf("p=%v: Encode: %v", p, err) + continue + } + m, err := Decode(&buf) + if err != nil { + t.Errorf("p=%v: Decode: %v", p, err) + continue + } + if got, want := m.Bounds(), image.Rect(0, 0, 6, 6); got != want { + t.Errorf("p=%v: got %v, want %v", p, got, want) + } + } +} + +func TestEncodeImplicitConfigSize(t *testing.T) { + // For backwards compatibility for Go 1.4 and earlier code, the Config + // field is optional, and if zero, the width and height is implied by the + // first (and in this case only) frame's width and height. + // + // A Config only specifies a width and height (two integers) while an + // image.Image's Bounds method returns an image.Rectangle (four integers). + // For a gif.GIF, the overall bounds' top-left point is always implicitly + // (0, 0), and any frame whose bounds have a negative X or Y will be + // outside those overall bounds, so encoding should fail. + for _, lowerBound := range []int{-1, 0, 1} { + images := []*image.Paletted{ + image.NewPaletted(image.Rect(lowerBound, lowerBound, 4, 4), palette.Plan9), + } + g := &GIF{ + Image: images, + Delay: make([]int, len(images)), + } + err := EncodeAll(ioutil.Discard, g) + if lowerBound >= 0 { + if err != nil { + t.Errorf("lowerBound=%d: %v", lowerBound, err) + } + } else { + if err == nil { + t.Errorf("lowerBound=%d: got nil error, want non-nil", lowerBound) + } + } + } +} + +func TestEncodePalettes(t *testing.T) { + const w, h = 5, 5 + pals := []color.Palette{{ + color.RGBA{0x00, 0x00, 0x00, 0xff}, + color.RGBA{0x01, 0x00, 0x00, 0xff}, + color.RGBA{0x02, 0x00, 0x00, 0xff}, + }, { + color.RGBA{0x00, 0x00, 0x00, 0xff}, + color.RGBA{0x00, 0x01, 0x00, 0xff}, + }, { + color.RGBA{0x00, 0x00, 0x03, 0xff}, + color.RGBA{0x00, 0x00, 0x02, 0xff}, + color.RGBA{0x00, 0x00, 0x01, 0xff}, + color.RGBA{0x00, 0x00, 0x00, 0xff}, + }, { + color.RGBA{0x10, 0x07, 0xf0, 0xff}, + color.RGBA{0x20, 0x07, 0xf0, 0xff}, + color.RGBA{0x30, 0x07, 0xf0, 0xff}, + color.RGBA{0x40, 0x07, 0xf0, 0xff}, + color.RGBA{0x50, 0x07, 0xf0, 0xff}, + }} + g0 := &GIF{ + Image: []*image.Paletted{ + image.NewPaletted(image.Rect(0, 0, w, h), pals[0]), + image.NewPaletted(image.Rect(0, 0, w, h), pals[1]), + image.NewPaletted(image.Rect(0, 0, w, h), pals[2]), + image.NewPaletted(image.Rect(0, 0, w, h), pals[3]), + }, + Delay: make([]int, len(pals)), + Disposal: make([]byte, len(pals)), + Config: image.Config{ + ColorModel: pals[2], + Width: w, + Height: h, + }, + } + + var buf bytes.Buffer + if err := EncodeAll(&buf, g0); err != nil { + t.Fatalf("EncodeAll: %v", err) + } + g1, err := DecodeAll(&buf) + if err != nil { + t.Fatalf("DecodeAll: %v", err) + } + if len(g0.Image) != len(g1.Image) { + t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image)) + } + for i, m := range g1.Image { + if got, want := m.Palette, pals[i]; !palettesEqual(got, want) { + t.Errorf("frame %d:\ngot %v\nwant %v", i, got, want) + } + } +} + func BenchmarkEncode(b *testing.B) { b.StopTimer() diff --git a/llgo/third_party/gofrontend/libgo/go/image/image.go b/llgo/third_party/gofrontend/libgo/go/image/image.go index 6b8e5c4877e..20b64d78e1a 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/image.go +++ b/llgo/third_party/gofrontend/libgo/go/image/image.go @@ -18,7 +18,7 @@ // initialization side effects. // // See "The Go image package" for more details: -// http://golang.org/doc/articles/image_package.html +// https://golang.org/doc/articles/image_package.html package image import ( @@ -46,9 +46,9 @@ type Image interface { } // PalettedImage is an image whose colors may come from a limited palette. -// If m is a PalettedImage and m.ColorModel() returns a PalettedColorModel p, +// If m is a PalettedImage and m.ColorModel() returns a color.Palette p, // then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's -// color model is not a PalettedColorModel, then ColorIndexAt's behavior is +// color model is not a color.Palette, then ColorIndexAt's behavior is // undefined. type PalettedImage interface { // ColorIndexAt returns the palette index of the pixel at (x, y). @@ -570,7 +570,7 @@ func NewAlpha(r Rectangle) *Alpha { return &Alpha{pix, 1 * w, r} } -// Alpha16 is an in-memory image whose At method returns color.Alpha64 values. +// Alpha16 is an in-memory image whose At method returns color.Alpha16 values. type Alpha16 struct { // Pix holds the image's pixels, as alpha values in big-endian format. The pixel at // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*2]. @@ -826,6 +826,92 @@ func NewGray16(r Rectangle) *Gray16 { return &Gray16{pix, 2 * w, r} } +// CMYK is an in-memory image whose At method returns color.CMYK values. +type CMYK struct { + // Pix holds the image's pixels, in C, M, Y, K order. The pixel at + // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]. + Pix []uint8 + // Stride is the Pix stride (in bytes) between vertically adjacent pixels. + Stride int + // Rect is the image's bounds. + Rect Rectangle +} + +func (p *CMYK) ColorModel() color.Model { return color.CMYKModel } + +func (p *CMYK) Bounds() Rectangle { return p.Rect } + +func (p *CMYK) At(x, y int) color.Color { + return p.CMYKAt(x, y) +} + +func (p *CMYK) CMYKAt(x, y int) color.CMYK { + if !(Point{x, y}.In(p.Rect)) { + return color.CMYK{} + } + i := p.PixOffset(x, y) + return color.CMYK{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], p.Pix[i+3]} +} + +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *CMYK) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 +} + +func (p *CMYK) Set(x, y int, c color.Color) { + if !(Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + c1 := color.CMYKModel.Convert(c).(color.CMYK) + p.Pix[i+0] = c1.C + p.Pix[i+1] = c1.M + p.Pix[i+2] = c1.Y + p.Pix[i+3] = c1.K +} + +func (p *CMYK) SetCMYK(x, y int, c color.CMYK) { + if !(Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + p.Pix[i+0] = c.C + p.Pix[i+1] = c.M + p.Pix[i+2] = c.Y + p.Pix[i+3] = c.K +} + +// SubImage returns an image representing the portion of the image p visible +// through r. The returned value shares pixels with the original image. +func (p *CMYK) SubImage(r Rectangle) Image { + r = r.Intersect(p.Rect) + // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside + // either r1 or r2 if the intersection is empty. Without explicitly checking for + // this, the Pix[i:] expression below can panic. + if r.Empty() { + return &CMYK{} + } + i := p.PixOffset(r.Min.X, r.Min.Y) + return &CMYK{ + Pix: p.Pix[i:], + Stride: p.Stride, + Rect: r, + } +} + +// Opaque scans the entire image and reports whether it is fully opaque. +func (p *CMYK) Opaque() bool { + return true +} + +// NewCMYK returns a new CMYK with the given bounds. +func NewCMYK(r Rectangle) *CMYK { + w, h := r.Dx(), r.Dy() + buf := make([]uint8, 4*w*h) + return &CMYK{buf, 4 * w, r} +} + // Paletted is an in-memory image of uint8 indices into a given palette. type Paletted struct { // Pix holds the image's pixels, as palette indices. The pixel at diff --git a/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/gen.go b/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/gen.go new file mode 100644 index 00000000000..fc1e707f0fd --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/gen.go @@ -0,0 +1,154 @@ +// Copyright 2015 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. + +// +build ignore + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/format" + "io/ioutil" + "log" + "os" +) + +var debug = flag.Bool("debug", false, "") + +func main() { + flag.Parse() + + w := new(bytes.Buffer) + w.WriteString(pre) + for _, sratio := range subsampleRatios { + fmt.Fprintf(w, sratioCase, sratio, sratioLines[sratio]) + } + w.WriteString(post) + + if *debug { + os.Stdout.Write(w.Bytes()) + return + } + out, err := format.Source(w.Bytes()) + if err != nil { + log.Fatal(err) + } + if err := ioutil.WriteFile("impl.go", out, 0660); err != nil { + log.Fatal(err) + } +} + +const pre = `// generated by "go run gen.go". DO NOT EDIT. + +package imageutil + +import ( + "image" +) + +// DrawYCbCr draws the YCbCr source image on the RGBA destination image with +// r.Min in dst aligned with sp in src. It reports whether the draw was +// successful. If it returns false, no dst pixels were changed. +// +// This function assumes that r is entirely within dst's bounds and the +// translation of r from dst coordinate space to src coordinate space is +// entirely within src's bounds. +func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) { + // This function exists in the image/internal/imageutil package because it + // is needed by both the image/draw and image/jpeg packages, but it doesn't + // seem right for one of those two to depend on the other. + // + // Another option is to have this code be exported in the image package, + // but we'd need to make sure we're totally happy with the API (for the + // rest of Go 1 compatibility), and decide if we want to have a more + // general purpose DrawToRGBA method for other image types. One possibility + // is: + // + // func (src *YCbCr) CopyToRGBA(dst *RGBA, dr, sr Rectangle) (effectiveDr, effectiveSr Rectangle) + // + // in the spirit of the built-in copy function for 1-dimensional slices, + // that also allowed a CopyFromRGBA method if needed. + + x0 := (r.Min.X - dst.Rect.Min.X) * 4 + x1 := (r.Max.X - dst.Rect.Min.X) * 4 + y0 := r.Min.Y - dst.Rect.Min.Y + y1 := r.Max.Y - dst.Rect.Min.Y + switch src.SubsampleRatio { +` + +const post = ` + default: + return false + } + return true +} +` + +const sratioCase = ` + case image.YCbCrSubsampleRatio%s: + for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + %s + + // This is an inline version of image/color/ycbcr.go's func YCbCrToRGB. + yy1 := int32(src.Y[yi]) * 0x10100 // 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 + } + if g < 0 { + g = 0 + } else if g > 255 { + g = 255 + } + if b < 0 { + b = 0 + } else if b > 255 { + b = 255 + } + + dpix[x+0] = uint8(r) + dpix[x+1] = uint8(g) + dpix[x+2] = uint8(b) + dpix[x+3] = 255 + } + } +` + +var subsampleRatios = []string{ + "444", + "422", + "420", + "440", +} + +var sratioLines = map[string]string{ + "444": ` + ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) + for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { + `, + "422": ` + ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2 + for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { + ci := ciBase + sx/2 + `, + "420": ` + ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2 + for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { + ci := ciBase + sx/2 + `, + "440": ` + ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X) + for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { + `, +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/imageutil.go b/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/imageutil.go new file mode 100644 index 00000000000..10cef0c6650 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/imageutil.go @@ -0,0 +1,8 @@ +// Copyright 2015 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. + +//go:generate go run gen.go + +// Package imageutil contains code shared by image-related packages. +package imageutil diff --git a/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/impl.go b/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/impl.go new file mode 100644 index 00000000000..fd7826d4a97 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/internal/imageutil/impl.go @@ -0,0 +1,196 @@ +// generated by "go run gen.go". DO NOT EDIT. + +package imageutil + +import ( + "image" +) + +// DrawYCbCr draws the YCbCr source image on the RGBA destination image with +// r.Min in dst aligned with sp in src. It reports whether the draw was +// successful. If it returns false, no dst pixels were changed. +// +// This function assumes that r is entirely within dst's bounds and the +// translation of r from dst coordinate space to src coordinate space is +// entirely within src's bounds. +func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) { + // This function exists in the image/internal/imageutil package because it + // is needed by both the image/draw and image/jpeg packages, but it doesn't + // seem right for one of those two to depend on the other. + // + // Another option is to have this code be exported in the image package, + // but we'd need to make sure we're totally happy with the API (for the + // rest of Go 1 compatibility), and decide if we want to have a more + // general purpose DrawToRGBA method for other image types. One possibility + // is: + // + // func (src *YCbCr) CopyToRGBA(dst *RGBA, dr, sr Rectangle) (effectiveDr, effectiveSr Rectangle) + // + // in the spirit of the built-in copy function for 1-dimensional slices, + // that also allowed a CopyFromRGBA method if needed. + + x0 := (r.Min.X - dst.Rect.Min.X) * 4 + x1 := (r.Max.X - dst.Rect.Min.X) * 4 + y0 := r.Min.Y - dst.Rect.Min.Y + y1 := r.Max.Y - dst.Rect.Min.Y + switch src.SubsampleRatio { + + case image.YCbCrSubsampleRatio444: + for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + + ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) + 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. + 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 + } + if g < 0 { + g = 0 + } else if g > 255 { + g = 255 + } + if b < 0 { + b = 0 + } else if b > 255 { + b = 255 + } + + dpix[x+0] = uint8(r) + dpix[x+1] = uint8(g) + dpix[x+2] = uint8(b) + dpix[x+3] = 255 + } + } + + case image.YCbCrSubsampleRatio422: + for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + + ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2 + for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { + 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. + 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 + } + if g < 0 { + g = 0 + } else if g > 255 { + g = 255 + } + if b < 0 { + b = 0 + } else if b > 255 { + b = 255 + } + + dpix[x+0] = uint8(r) + dpix[x+1] = uint8(g) + dpix[x+2] = uint8(b) + dpix[x+3] = 255 + } + } + + case image.YCbCrSubsampleRatio420: + for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + + ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2 + for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { + 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. + 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 + } + if g < 0 { + g = 0 + } else if g > 255 { + g = 255 + } + if b < 0 { + b = 0 + } else if b > 255 { + b = 255 + } + + dpix[x+0] = uint8(r) + dpix[x+1] = uint8(g) + dpix[x+2] = uint8(b) + dpix[x+3] = 255 + } + } + + case image.YCbCrSubsampleRatio440: + for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + + ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X) + 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. + 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 + } + if g < 0 { + g = 0 + } else if g > 255 { + g = 255 + } + if b < 0 { + b = 0 + } else if b > 255 { + b = 255 + } + + dpix[x+0] = uint8(r) + dpix[x+1] = uint8(g) + dpix[x+2] = uint8(b) + dpix[x+3] = 255 + } + } + + default: + return false + } + return true +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/jpeg/huffman.go b/llgo/third_party/gofrontend/libgo/go/image/jpeg/huffman.go index d4ff4cfa0ce..4f8fe8eff32 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/jpeg/huffman.go +++ b/llgo/third_party/gofrontend/libgo/go/image/jpeg/huffman.go @@ -187,7 +187,9 @@ func (d *decoder) decodeHuffman(h *huffman) (uint8, error) { // There are no more bytes of data in this segment, but we may still // be able to read the next symbol out of the previously read bits. // First, undo the readByte that the ensureNBits call made. - d.unreadByteStuffedByte() + if d.bytes.nUnreadable != 0 { + d.unreadByteStuffedByte() + } goto slowPath } } diff --git a/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader.go b/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader.go index 6d8b1d1d036..adf97abbd1d 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader.go +++ b/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader.go @@ -10,6 +10,7 @@ package jpeg import ( "image" "image/color" + "image/internal/imageutil" "io" ) @@ -26,6 +27,8 @@ type UnsupportedError string func (e UnsupportedError) Error() string { return "unsupported JPEG feature: " + string(e) } +var errUnsupportedSubsamplingRatio = UnsupportedError("luma/chroma subsampling ratio") + // Component specification, specified in section B.2.2. type component struct { h int // Horizontal sampling factor. @@ -41,32 +44,35 @@ const ( maxTh = 3 maxTq = 3 - // A grayscale JPEG image has only a Y component. - nGrayComponent = 1 - // A color JPEG image has Y, Cb and Cr components. - nColorComponent = 3 + maxComponents = 4 +) - // 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 - maxV = 2 +const ( + sof0Marker = 0xc0 // Start Of Frame (Baseline). + sof1Marker = 0xc1 // Start Of Frame (Extended Sequential). + sof2Marker = 0xc2 // Start Of Frame (Progressive). + dhtMarker = 0xc4 // Define Huffman Table. + rst0Marker = 0xd0 // ReSTart (0). + rst7Marker = 0xd7 // ReSTart (7). + soiMarker = 0xd8 // Start Of Image. + eoiMarker = 0xd9 // End Of Image. + sosMarker = 0xda // Start Of Scan. + dqtMarker = 0xdb // Define Quantization Table. + driMarker = 0xdd // Define Restart Interval. + comMarker = 0xfe // COMment. + // "APPlication specific" markers aren't part of the JPEG spec per se, + // but in practice, their use is described at + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html + app0Marker = 0xe0 + app14Marker = 0xee + app15Marker = 0xef ) +// See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe const ( - soiMarker = 0xd8 // Start Of Image. - eoiMarker = 0xd9 // End Of Image. - sof0Marker = 0xc0 // Start Of Frame (Baseline). - sof2Marker = 0xc2 // Start Of Frame (Progressive). - dhtMarker = 0xc4 // Define Huffman Table. - dqtMarker = 0xdb // Define Quantization Table. - sosMarker = 0xda // Start Of Scan. - driMarker = 0xdd // Define Restart Interval. - rst0Marker = 0xd0 // ReSTart (0). - rst7Marker = 0xd7 // ReSTart (7). - app0Marker = 0xe0 // APPlication specific (0). - app15Marker = 0xef // APPlication specific (15). - comMarker = 0xfe // COMment. + adobeTransformUnknown = 0 + adobeTransformYCbCr = 1 + adobeTransformYCbCrK = 2 ) // unzig maps from the zig-zag ordering to the natural ordering. For example, @@ -83,7 +89,7 @@ var unzig = [blockSize]int{ 53, 60, 61, 54, 47, 55, 62, 63, } -// Reader is deprecated. +// Deprecated: Reader is deprecated. type Reader interface { io.ByteReader io.Reader @@ -114,17 +120,25 @@ type decoder struct { nUnreadable int } width, height int - img1 *image.Gray - 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 [blockSize + 1]byte + + img1 *image.Gray + img3 *image.YCbCr + blackPix []byte + blackStride int + + ri int // Restart Interval. + nComp int + progressive bool + jfif bool + adobeTransformValid bool + adobeTransform uint8 + eobRun uint16 // End-of-Band run, specified in section G.1.2.2. + + comp [maxComponents]component + progCoeffs [maxComponents][]block // Saved state between progressive-mode scans. + huff [maxTc + 1][maxTh + 1]huffman + quant [maxTq + 1]block // Quantization tables, in zig-zag order. + tmp [2 * blockSize]byte } // fill fills up the d.bytes.buf buffer from the underlying io.Reader. It @@ -155,9 +169,6 @@ func (d *decoder) fill() error { // sometimes overshoot and read one or two too many bytes. Two-byte overshoot // can happen when expecting to read a 0xff 0x00 byte-stuffed byte. func (d *decoder) unreadByteStuffedByte() { - if d.bytes.nUnreadable == 0 { - panic("jpeg: unreadByteStuffedByte call cannot be fulfilled") - } d.bytes.i -= d.bytes.nUnreadable d.bytes.nUnreadable = 0 if d.bits.n >= 8 { @@ -203,18 +214,19 @@ func (d *decoder) readByteStuffedByte() (x byte, err error) { return 0xff, nil } + d.bytes.nUnreadable = 0 + x, err = d.readByte() if err != nil { return 0, err } + d.bytes.nUnreadable = 1 if x != 0xff { - d.bytes.nUnreadable = 1 return x, nil } x, err = d.readByte() if err != nil { - d.bytes.nUnreadable = 1 return 0, err } d.bytes.nUnreadable = 2 @@ -284,13 +296,18 @@ func (d *decoder) ignore(n int) error { // Specified in section B.2.2. func (d *decoder) processSOF(n int) error { + if d.nComp != 0 { + return FormatError("multiple SOF markers") + } switch n { - case 6 + 3*nGrayComponent: - d.nComp = nGrayComponent - case 6 + 3*nColorComponent: - d.nComp = nColorComponent + case 6 + 3*1: // Grayscale image. + d.nComp = 1 + case 6 + 3*3: // YCbCr or RGB image. + d.nComp = 3 + case 6 + 3*4: // YCbCrK or CMYK image. + d.nComp = 4 default: - return UnsupportedError("SOF has wrong length") + return UnsupportedError("number of components") } if err := d.readFull(d.tmp[:n]); err != nil { return err @@ -302,12 +319,34 @@ func (d *decoder) processSOF(n int) error { d.height = int(d.tmp[1])<<8 + int(d.tmp[2]) d.width = int(d.tmp[3])<<8 + int(d.tmp[4]) if int(d.tmp[5]) != d.nComp { - return UnsupportedError("SOF has wrong number of image components") + return FormatError("SOF has wrong length") } + for i := 0; i < d.nComp; i++ { d.comp[i].c = d.tmp[6+3*i] + // Section B.2.2 states that "the value of C_i shall be different from + // the values of C_1 through C_(i-1)". + for j := 0; j < i; j++ { + if d.comp[i].c == d.comp[j].c { + return FormatError("repeated component identifier") + } + } + d.comp[i].tq = d.tmp[8+3*i] - if d.nComp == nGrayComponent { + if d.comp[i].tq > maxTq { + return FormatError("bad Tq value") + } + + hv := d.tmp[7+3*i] + h, v := int(hv>>4), int(hv&0x0f) + if h < 1 || 4 < h || v < 1 || 4 < v { + return FormatError("luma/chroma subsampling ratio") + } + if h == 3 || v == 3 { + return errUnsupportedSubsamplingRatio + } + switch d.nComp { + case 1: // If a JPEG image has only one component, section A.2 says "this data // is non-interleaved by definition" and section A.2.2 says "[in this // case...] the order of data units within a scan shall be left-to-right @@ -319,45 +358,104 @@ func (d *decoder) processSOF(n int) error { // always 1. The component's (h, v) is effectively always (1, 1): even if // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 // MCUs, not two 16x8 MCUs. - d.comp[i].h = 1 - d.comp[i].v = 1 - continue - } - hv := d.tmp[7+3*i] - d.comp[i].h = int(hv >> 4) - d.comp[i].v = int(hv & 0x0f) - // 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), (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 && hv != 0x12 { - return UnsupportedError("luma/chroma downsample ratio") + h, v = 1, 1 + + case 3: + // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, + // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the + // (h, v) values for the Y component are either (1, 1), (1, 2), + // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values + // must be a multiple of the Cb and Cr component's values. We also + // assume that the two chroma components have the same subsampling + // ratio. + switch i { + case 0: // Y. + // We have already verified, above, that h and v are both + // either 1, 2 or 4, so invalid (h, v) combinations are those + // with v == 4. + if v == 4 { + return errUnsupportedSubsamplingRatio + } + case 1: // Cb. + if d.comp[0].h%h != 0 || d.comp[0].v%v != 0 { + return errUnsupportedSubsamplingRatio + } + case 2: // Cr. + if d.comp[1].h != h || d.comp[1].v != v { + return errUnsupportedSubsamplingRatio + } + } + + case 4: + // For 4-component images (either CMYK or YCbCrK), we only support two + // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. + // Theoretically, 4-component JPEG images could mix and match hv values + // but in practice, those two combinations are the only ones in use, + // and it simplifies the applyBlack code below if we can assume that: + // - for CMYK, the C and K channels have full samples, and if the M + // and Y channels subsample, they subsample both horizontally and + // vertically. + // - for YCbCrK, the Y and K channels have full samples. + switch i { + case 0: + if hv != 0x11 && hv != 0x22 { + return errUnsupportedSubsamplingRatio + } + case 1, 2: + if hv != 0x11 { + return errUnsupportedSubsamplingRatio + } + case 3: + if d.comp[0].h != h || d.comp[0].v != v { + return errUnsupportedSubsamplingRatio + } } - } else if hv != 0x11 { - return UnsupportedError("luma/chroma downsample ratio") } + + d.comp[i].h = h + d.comp[i].v = v } return nil } // Specified in section B.2.4.1. func (d *decoder) processDQT(n int) error { - const qtLength = 1 + blockSize - for ; n >= qtLength; n -= qtLength { - if err := d.readFull(d.tmp[:qtLength]); err != nil { +loop: + for n > 0 { + n-- + x, err := d.readByte() + if err != nil { return err } - pq := d.tmp[0] >> 4 - if pq != 0 { - return UnsupportedError("bad Pq value") - } - tq := d.tmp[0] & 0x0f + tq := x & 0x0f if tq > maxTq { return FormatError("bad Tq value") } - for i := range d.quant[tq] { - d.quant[tq][i] = int32(d.tmp[i+1]) + switch x >> 4 { + default: + return FormatError("bad Pq value") + case 0: + if n < blockSize { + break loop + } + n -= blockSize + if err := d.readFull(d.tmp[:blockSize]); err != nil { + return err + } + for i := range d.quant[tq] { + d.quant[tq][i] = int32(d.tmp[i]) + } + case 1: + if n < 2*blockSize { + break loop + } + n -= 2 * blockSize + if err := d.readFull(d.tmp[:2*blockSize]); err != nil { + return err + } + for i := range d.quant[tq] { + d.quant[tq][i] = int32(d.tmp[2*i])<<8 | int32(d.tmp[2*i+1]) + } } } if n != 0 { @@ -378,6 +476,43 @@ func (d *decoder) processDRI(n int) error { return nil } +func (d *decoder) processApp0Marker(n int) error { + if n < 5 { + return d.ignore(n) + } + if err := d.readFull(d.tmp[:5]); err != nil { + return err + } + n -= 5 + + d.jfif = d.tmp[0] == 'J' && d.tmp[1] == 'F' && d.tmp[2] == 'I' && d.tmp[3] == 'F' && d.tmp[4] == '\x00' + + if n > 0 { + return d.ignore(n) + } + return nil +} + +func (d *decoder) processApp14Marker(n int) error { + if n < 12 { + return d.ignore(n) + } + if err := d.readFull(d.tmp[:12]); err != nil { + return err + } + n -= 12 + + if d.tmp[0] == 'A' && d.tmp[1] == 'd' && d.tmp[2] == 'o' && d.tmp[3] == 'b' && d.tmp[4] == 'e' { + d.adobeTransformValid = true + d.adobeTransform = d.tmp[11] + } + + if n > 0 { + return d.ignore(n) + } + return nil +} + // decode reads a JPEG image from r and returns it as an image.Image. func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { d.r = r @@ -459,25 +594,48 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { return nil, FormatError("short segment length") } - switch { - case marker == sof0Marker || marker == sof2Marker: // Start Of Frame. + switch marker { + case sof0Marker, sof1Marker, sof2Marker: d.progressive = marker == sof2Marker err = d.processSOF(n) - if configOnly { + if configOnly && d.jfif { return nil, err } - case marker == dhtMarker: // Define Huffman Table. - err = d.processDHT(n) - case marker == dqtMarker: // Define Quantization Table. - err = d.processDQT(n) - case marker == sosMarker: // Start Of Scan. + case dhtMarker: + if configOnly { + err = d.ignore(n) + } else { + err = d.processDHT(n) + } + case dqtMarker: + if configOnly { + err = d.ignore(n) + } else { + err = d.processDQT(n) + } + case sosMarker: + if configOnly { + return nil, nil + } err = d.processSOS(n) - case marker == driMarker: // Define Restart Interval. - err = d.processDRI(n) - case app0Marker <= marker && marker <= app15Marker || marker == comMarker: // APPlication specific, or COMment. - err = d.ignore(n) + case driMarker: + if configOnly { + err = d.ignore(n) + } else { + err = d.processDRI(n) + } + case app0Marker: + err = d.processApp0Marker(n) + case app14Marker: + err = d.processApp14Marker(n) default: - err = UnsupportedError("unknown marker") + if app0Marker <= marker && marker <= app15Marker || marker == comMarker { + err = d.ignore(n) + } else if marker < 0xc0 { // See Table B.1 "Marker code assignments". + err = FormatError("unknown marker") + } else { + err = UnsupportedError("unknown marker") + } } if err != nil { return nil, err @@ -487,11 +645,118 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { return d.img1, nil } if d.img3 != nil { + if d.blackPix != nil { + return d.applyBlack() + } else if d.isRGB() { + return d.convertToRGB() + } return d.img3, nil } return nil, FormatError("missing SOS marker") } +// applyBlack combines d.img3 and d.blackPix into a CMYK image. The formula +// used depends on whether the JPEG image is stored as CMYK or YCbCrK, +// indicated by the APP14 (Adobe) metadata. +// +// Adobe CMYK JPEG images are inverted, where 255 means no ink instead of full +// ink, so we apply "v = 255 - v" at various points. Note that a double +// inversion is a no-op, so inversions might be implicit in the code below. +func (d *decoder) applyBlack() (image.Image, error) { + if !d.adobeTransformValid { + return nil, UnsupportedError("unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata") + } + + // If the 4-component JPEG image isn't explicitly marked as "Unknown (RGB + // or CMYK)" as per + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + // we assume that it is YCbCrK. This matches libjpeg's jdapimin.c. + if d.adobeTransform != adobeTransformUnknown { + // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get + // CMY, and patch in the original K. The RGB to CMY inversion cancels + // out the 'Adobe inversion' described in the applyBlack doc comment + // above, so in practice, only the fourth channel (black) is inverted. + bounds := d.img3.Bounds() + img := image.NewRGBA(bounds) + imageutil.DrawYCbCr(img, bounds, d.img3, bounds.Min) + for iBase, y := 0, bounds.Min.Y; y < bounds.Max.Y; iBase, y = iBase+img.Stride, y+1 { + for i, x := iBase+3, bounds.Min.X; x < bounds.Max.X; i, x = i+4, x+1 { + img.Pix[i] = 255 - d.blackPix[(y-bounds.Min.Y)*d.blackStride+(x-bounds.Min.X)] + } + } + return &image.CMYK{ + Pix: img.Pix, + Stride: img.Stride, + Rect: img.Rect, + }, nil + } + + // The first three channels (cyan, magenta, yellow) of the CMYK + // were decoded into d.img3, but each channel was decoded into a separate + // []byte slice, and some channels may be subsampled. We interleave the + // separate channels into an image.CMYK's single []byte slice containing 4 + // contiguous bytes per pixel. + bounds := d.img3.Bounds() + img := image.NewCMYK(bounds) + + translations := [4]struct { + src []byte + stride int + }{ + {d.img3.Y, d.img3.YStride}, + {d.img3.Cb, d.img3.CStride}, + {d.img3.Cr, d.img3.CStride}, + {d.blackPix, d.blackStride}, + } + for t, translation := range translations { + subsample := d.comp[t].h != d.comp[0].h || d.comp[t].v != d.comp[0].v + for iBase, y := 0, bounds.Min.Y; y < bounds.Max.Y; iBase, y = iBase+img.Stride, y+1 { + sy := y - bounds.Min.Y + if subsample { + sy /= 2 + } + for i, x := iBase+t, bounds.Min.X; x < bounds.Max.X; i, x = i+4, x+1 { + sx := x - bounds.Min.X + if subsample { + sx /= 2 + } + img.Pix[i] = 255 - translation.src[sy*translation.stride+sx] + } + } + } + return img, nil +} + +func (d *decoder) isRGB() bool { + if d.jfif { + return false + } + if d.adobeTransformValid && d.adobeTransform == adobeTransformUnknown { + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr. + return true + } + return d.comp[0].c == 'R' && d.comp[1].c == 'G' && d.comp[2].c == 'B' +} + +func (d *decoder) convertToRGB() (image.Image, error) { + cScale := d.comp[0].h / d.comp[1].h + bounds := d.img3.Bounds() + img := image.NewRGBA(bounds) + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + po := img.PixOffset(bounds.Min.X, y) + yo := d.img3.YOffset(bounds.Min.X, y) + co := d.img3.COffset(bounds.Min.X, y) + for i, iMax := 0, bounds.Max.X-bounds.Min.X; i < iMax; i++ { + img.Pix[po+4*i+0] = d.img3.Y[yo+i] + img.Pix[po+4*i+1] = d.img3.Cb[co+i/cScale] + img.Pix[po+4*i+2] = d.img3.Cr[co+i/cScale] + img.Pix[po+4*i+3] = 255 + } + } + return img, nil +} + // Decode reads a JPEG image from r and returns it as an image.Image. func Decode(r io.Reader) (image.Image, error) { var d decoder @@ -506,15 +771,25 @@ func DecodeConfig(r io.Reader) (image.Config, error) { return image.Config{}, err } switch d.nComp { - case nGrayComponent: + case 1: return image.Config{ ColorModel: color.GrayModel, Width: d.width, Height: d.height, }, nil - case nColorComponent: + case 3: + cm := color.YCbCrModel + if d.isRGB() { + cm = color.RGBAModel + } + return image.Config{ + ColorModel: cm, + Width: d.width, + Height: d.height, + }, nil + case 4: return image.Config{ - ColorModel: color.YCbCrModel, + ColorModel: color.CMYKModel, Width: d.width, Height: d.height, }, nil diff --git a/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader_test.go b/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader_test.go index 4de2e8ee737..77376152bc0 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/jpeg/reader_test.go @@ -15,6 +15,7 @@ import ( "os" "strings" "testing" + "time" ) // TestDecodeProgressive tests that decoding the baseline and progressive @@ -23,6 +24,8 @@ import ( func TestDecodeProgressive(t *testing.T) { testCases := []string{ "../testdata/video-001", + "../testdata/video-001.q50.410", + "../testdata/video-001.q50.411", "../testdata/video-001.q50.420", "../testdata/video-001.q50.422", "../testdata/video-001.q50.440", @@ -184,6 +187,81 @@ func pixString(pix []byte, stride, x, y int) string { return s.String() } +func TestTruncatedSOSDataDoesntPanic(t *testing.T) { + b, err := ioutil.ReadFile("../testdata/video-005.gray.q50.jpeg") + if err != nil { + t.Fatal(err) + } + sosMarker := []byte{0xff, 0xda} + i := bytes.Index(b, sosMarker) + if i < 0 { + t.Fatal("SOS marker not found") + } + i += len(sosMarker) + j := i + 10 + if j > len(b) { + j = len(b) + } + for ; i < j; i++ { + Decode(bytes.NewReader(b[:i])) + } +} + +func TestLargeImageWithShortData(t *testing.T) { + // This input is an invalid JPEG image, based on the fuzzer-generated image + // in issue 10413. It is only 504 bytes, and shouldn't take long for Decode + // to return an error. The Start Of Frame marker gives the image dimensions + // as 8192 wide and 8192 high, so even if an unreadByteStuffedByte bug + // doesn't technically lead to an infinite loop, such a bug can still cause + // an unreasonably long loop for such a short input. + const input = "" + + "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01" + + "\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10" + + "\x0e\x89\x0e\x12\x11\x10\x13\x18\xff\xd8\xff\xe0\x00\x10\x4a\x46" + + "\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43" + + "\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10\x13\x18" + + "\x28\x1a\x18\x16\x16\x18\x31\x23\x25\x1d\x28\x3a\x33\x3d\x3c\x39" + + "\x33\x38\x37\x40\x48\x5c\x4e\x40\x44\x57\x45\x37\x38\x50\x6d\x51" + + "\x57\x5f\x62\x67\x68\x67\x3e\x4d\x71\x79\x70\x64\x78\x5c\x65\x67" + + "\x63\xff\xc0\x00\x0b\x08\x20\x00\x20\x00\x01\x01\x11\x00\xff\xc4" + + "\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff" + + "\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04" + + "\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x01\x06" + + "\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\xd8\xff\xdd" + + "\x42\xb1\xc1\x15\x52\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17" + + "\x18\x19\x1a\x25\x26\x27\x28\x29\x2a\x34\x35\x36\x37\x38\x39\x3a" + + "\x43\x44\x45\x46\x47\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a" + + "\x00\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75\x76\x77\x78\x79" + + "\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98" + + "\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6" + + "\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xff\xd8\xff\xe0\x00\x10" + + "\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb" + + "\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10" + + "\x13\x18\x28\x1a\x18\x16\x16\x18\x31\x23\x25\x1d\xc8\xc9\xca\xd2" + + "\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" + + "\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x08" + + "\x01\x01\x00\x00\x3f\x00\xb9\xeb\x50\xb0\xdb\xc8\xa8\xe4\x63\x80" + + "\xdd\x31\xd6\x9d\xbb\xf2\xc5\x42\x1f\x6c\x6f\xf4\x34\xdd\x3c\xfc" + + "\xac\xe7\x3d\x80\xa9\xcc\x87\x34\xb3\x37\xfa\x2b\x9f\x6a\xad\x63" + + "\x20\x36\x9f\x78\x64\x75\xe6\xab\x7d\xb2\xde\x29\x70\xd3\x20\x27" + + "\xde\xaf\xa4\xf0\xca\x9f\x24\xa8\xdf\x46\xa8\x24\x84\x96\xe3\x77" + + "\xf9\x2e\xe0\x0a\x62\x7f\xdf\xd9" + c := make(chan error, 1) + go func() { + _, err := Decode(strings.NewReader(input)) + c <- err + }() + select { + case err := <-c: + if err == nil { + t.Fatalf("got nil error, want non-nil") + } + case <-time.After(3 * time.Second): + t.Fatalf("timed out") + } +} + func TestExtraneousData(t *testing.T) { // Encode a 1x1 red image. src := image.NewRGBA(image.Rect(0, 0, 1, 1)) diff --git a/llgo/third_party/gofrontend/libgo/go/image/jpeg/scan.go b/llgo/third_party/gofrontend/libgo/go/image/jpeg/scan.go index 2bd1d9d531d..99734c01af0 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/jpeg/scan.go +++ b/llgo/third_party/gofrontend/libgo/go/image/jpeg/scan.go @@ -9,27 +9,42 @@ import ( ) // makeImg allocates and initializes the destination image. -func (d *decoder) makeImg(h0, v0, mxx, myy int) { - if d.nComp == nGrayComponent { +func (d *decoder) makeImg(mxx, myy int) { + if d.nComp == 1 { m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy)) d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray) return } + + h0 := d.comp[0].h + v0 := d.comp[0].v + hRatio := h0 / d.comp[1].h + vRatio := v0 / d.comp[1].v var subsampleRatio image.YCbCrSubsampleRatio - switch { - case h0 == 1 && v0 == 1: + switch hRatio<<4 | vRatio { + case 0x11: subsampleRatio = image.YCbCrSubsampleRatio444 - case h0 == 1 && v0 == 2: + case 0x12: subsampleRatio = image.YCbCrSubsampleRatio440 - case h0 == 2 && v0 == 1: + case 0x21: subsampleRatio = image.YCbCrSubsampleRatio422 - case h0 == 2 && v0 == 2: + case 0x22: subsampleRatio = image.YCbCrSubsampleRatio420 + case 0x41: + subsampleRatio = image.YCbCrSubsampleRatio411 + case 0x42: + subsampleRatio = image.YCbCrSubsampleRatio410 default: panic("unreachable") } 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) + + if d.nComp == 4 { + h3, v3 := d.comp[3].h, d.comp[3].v + d.blackPix = make([]byte, 8*h3*mxx*8*v3*myy) + d.blackStride = 8 * h3 * mxx + } } // Specified in section B.2.3. @@ -47,15 +62,16 @@ func (d *decoder) processSOS(n int) error { if n != 4+2*nComp { return FormatError("SOS length inconsistent with number of components") } - var scan [nColorComponent]struct { + var scan [maxComponents]struct { compIndex uint8 td uint8 // DC table selector. ta uint8 // AC table selector. } + totalHV := 0 for i := 0; i < nComp; i++ { cs := d.tmp[1+2*i] // Component selector. compIndex := -1 - for j, comp := range d.comp { + for j, comp := range d.comp[:d.nComp] { if cs == comp.c { compIndex = j } @@ -64,6 +80,18 @@ func (d *decoder) processSOS(n int) error { return FormatError("unknown component selector") } scan[i].compIndex = uint8(compIndex) + // Section B.2.3 states that "the value of Cs_j shall be different from + // the values of Cs_1 through Cs_(j-1)". Since we have previously + // verified that a frame's component identifiers (C_i values in section + // B.2.2) are unique, it suffices to check that the implicit indexes + // into d.comp are unique. + for j := 0; j < i; j++ { + if scan[i].compIndex == scan[j].compIndex { + return FormatError("repeated component selector") + } + } + totalHV += d.comp[compIndex].h * d.comp[compIndex].v + scan[i].td = d.tmp[2+2*i] >> 4 if scan[i].td > maxTh { return FormatError("bad Td value") @@ -73,6 +101,11 @@ func (d *decoder) processSOS(n int) error { return FormatError("bad Ta value") } } + // Section B.2.3 states that if there is more than one component then the + // total H*V values in a scan must be <= 10. + if d.nComp > 1 && totalHV > 10 { + return FormatError("total sampling factors too large") + } // zigStart and zigEnd are the spectral selection bounds. // ah and al are the successive approximation high and low values. @@ -112,7 +145,7 @@ func (d *decoder) processSOS(n int) error { 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) + d.makeImg(mxx, myy) } if d.progressive { for i := 0; i < nComp; i++ { @@ -128,11 +161,9 @@ func (d *decoder) processSOS(n int) error { var ( // b is the decoded coefficients, in natural (not zig-zag) order. b block - dc [nColorComponent]int32 - // bx and by 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: - // bx is 2 and by is 0, even though the pixel is in the second MCU. + dc [maxComponents]int32 + // bx and by are the location of the current block, in units of 8x8 + // blocks: the third block in the first row has (bx, by) = (2, 0). bx, by int blockCount int ) @@ -140,8 +171,10 @@ func (d *decoder) processSOS(n int) error { for mx := 0; mx < mxx; mx++ { for i := 0; i < nComp; i++ { 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 < d.comp[compIndex].h*d.comp[compIndex].v; j++ { + 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. // @@ -168,15 +201,10 @@ func (d *decoder) processSOS(n int) error { // 0 1 2 // 3 4 5 if nComp != 1 { - bx, by = d.comp[compIndex].h*mx, d.comp[compIndex].v*my - if h0 == 1 { - by += j - } else { - bx += j % 2 - by += j / 2 - } + bx = hi*mx + j%hi + by = vi*my + j/hi } else { - q := mxx * d.comp[compIndex].h + q := mxx * hi bx = blockCount % q by = blockCount / q blockCount++ @@ -187,7 +215,7 @@ func (d *decoder) processSOS(n int) error { // Load the previous partially decoded coefficients, if applicable. if d.progressive { - b = d.progCoeffs[compIndex][by*mxx*d.comp[compIndex].h+bx] + b = d.progCoeffs[compIndex][by*mxx*hi+bx] } else { b = block{} } @@ -260,7 +288,7 @@ 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*d.comp[compIndex].h+bx] = b + 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, @@ -276,7 +304,7 @@ func (d *decoder) processSOS(n int) error { } idct(&b) dst, stride := []byte(nil), 0 - if d.nComp == nGrayComponent { + if d.nComp == 1 { dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride } else { switch compIndex { @@ -286,6 +314,8 @@ func (d *decoder) processSOS(n int) error { 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") } @@ -325,7 +355,7 @@ func (d *decoder) processSOS(n int) error { // Reset the Huffman decoder. d.bits = bits{} // Reset the DC components, as per section F.2.1.3.1. - dc = [nColorComponent]int32{} + dc = [maxComponents]int32{} // Reset the progressive decoder state, as per section G.1.2.2. d.eobRun = 0 } diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/reader.go b/llgo/third_party/gofrontend/libgo/go/image/png/reader.go index 0a40ca161d9..bbd6f753fad 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/png/reader.go +++ b/llgo/third_party/gofrontend/libgo/go/image/png/reader.go @@ -47,6 +47,10 @@ const ( cbTCA16 ) +func cbPaletted(cb int) bool { + return cbP1 <= cb && cb <= cbP8 +} + // Filter type, as per the PNG spec. const ( ftNone = 0 @@ -81,15 +85,16 @@ var interlacing = []interlaceScan{ } // Decoding stage. -// The PNG specification says that the IHDR, PLTE (if present), IDAT and IEND -// chunks must appear in that order. There may be multiple IDAT chunks, and -// IDAT chunks must be sequential (i.e. they may not have any other chunks -// between them). +// The PNG specification says that the IHDR, PLTE (if present), tRNS (if +// present), IDAT and IEND chunks must appear in that order. There may be +// multiple IDAT chunks, and IDAT chunks must be sequential (i.e. they may not +// have any other chunks between them). // http://www.w3.org/TR/PNG/#5ChunkOrdering const ( dsStart = iota dsSeenIHDR dsSeenPLTE + dsSeentRNS dsSeenIDAT dsSeenIEND ) @@ -321,15 +326,23 @@ func (d *decoder) decode() (image.Image, error) { var img image.Image if d.interlace == itNone { img, err = d.readImagePass(r, 0, false) + if err != nil { + return nil, err + } } else if d.interlace == itAdam7 { // Allocate a blank image of the full size. img, err = d.readImagePass(nil, 0, true) + if err != nil { + return nil, err + } for pass := 0; pass < 7; pass++ { imagePass, err := d.readImagePass(r, pass, false) if err != nil { return nil, err } - d.mergePassInto(img, imagePass, pass) + if imagePass != nil { + d.mergePassInto(img, imagePass, pass) + } } } @@ -371,6 +384,12 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image // Add the multiplication factor and subtract one, effectively rounding up. width = (width - p.xOffset + p.xFactor - 1) / p.xFactor height = (height - p.yOffset + p.yFactor - 1) / p.yFactor + // A PNG image can't have zero width or height, but for an interlaced + // image, an individual pass might have zero width or height. If so, we + // shouldn't even read a per-row filter type byte, so return early. + if width == 0 || height == 0 { + return nil, nil + } } switch d.cb { case cbG1, cbG2, cbG4, cbG8: @@ -425,6 +444,9 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image // Read the decompressed bytes. _, err := io.ReadFull(r, cr) if err != nil { + if err == io.EOF || err == io.ErrUnexpectedEOF { + return nil, FormatError("not enough pixel data") + } return nil, err } @@ -443,6 +465,9 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image cdat[i] += p } case ftAverage: + // The first column has no column to the left of it, so it is a + // special case. We know that the first column exists because we + // check above that width != 0, and so len(cdat) != 0. for i := 0; i < bytesPerPixel; i++ { cdat[i] += pdat[i] / 2 } @@ -687,9 +712,10 @@ func (d *decoder) parseChunk() error { if d.stage != dsSeenPLTE { return chunkOrderError } + d.stage = dsSeentRNS return d.parsetRNS(length) case "IDAT": - if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) { + if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.stage == dsSeenIHDR && cbPaletted(d.cb)) { return chunkOrderError } d.stage = dsSeenIDAT @@ -779,7 +805,7 @@ func DecodeConfig(r io.Reader) (image.Config, error) { } return image.Config{}, err } - paletted := d.cb == cbP8 || d.cb == cbP4 || d.cb == cbP2 || d.cb == cbP1 + paletted := cbPaletted(d.cb) if d.stage == dsSeenIHDR && !paletted { break } diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/reader_test.go b/llgo/third_party/gofrontend/libgo/go/image/png/reader_test.go index ce772eb6f09..f89e7efe7fe 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/png/reader_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/png/reader_test.go @@ -6,12 +6,14 @@ package png import ( "bufio" + "bytes" "fmt" "image" "image/color" "io" "io/ioutil" "os" + "reflect" "strings" "testing" ) @@ -319,6 +321,93 @@ func TestPalettedDecodeConfig(t *testing.T) { } } +func TestInterlaced(t *testing.T) { + a, err := readPNG("testdata/gray-gradient.png") + if err != nil { + t.Fatal(err) + } + b, err := readPNG("testdata/gray-gradient.interlaced.png") + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(a, b) { + t.Fatalf("decodings differ:\nnon-interlaced:\n%#v\ninterlaced:\n%#v", a, b) + } +} + +func TestIncompleteIDATOnRowBoundary(t *testing.T) { + // The following is an invalid 1x2 grayscale PNG image. The header is OK, + // but the zlib-compressed IDAT payload contains two bytes "\x02\x00", + // which is only one row of data (the leading "\x02" is a row filter). + const ( + ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x02\x08\x00\x00\x00\x00\xbc\xea\xe9\xfb" + idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" + iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" + ) + _, err := Decode(strings.NewReader(pngHeader + ihdr + idat + iend)) + if err == nil { + t.Fatal("got nil error, want non-nil") + } +} + +func TestMultipletRNSChunks(t *testing.T) { + /* + The following is a valid 1x1 paletted PNG image with a 1-element palette + containing color.NRGBA{0xff, 0x00, 0x00, 0x7f}: + 0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR + 0000010: 0000 0001 0000 0001 0803 0000 0028 cb34 .............(.4 + 0000020: bb00 0000 0350 4c54 45ff 0000 19e2 0937 .....PLTE......7 + 0000030: 0000 0001 7452 4e53 7f80 5cb4 cb00 0000 ....tRNS..\..... + 0000040: 0e49 4441 5478 9c62 6200 0400 00ff ff00 .IDATx.bb....... + 0000050: 0600 03fa d059 ae00 0000 0049 454e 44ae .....Y.....IEND. + 0000060: 4260 82 B`. + Dropping the tRNS chunk makes that color's alpha 0xff instead of 0x7f. + */ + const ( + ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x03\x00\x00\x00\x28\xcb\x34\xbb" + plte = "\x00\x00\x00\x03PLTE\xff\x00\x00\x19\xe2\x09\x37" + trns = "\x00\x00\x00\x01tRNS\x7f\x80\x5c\xb4\xcb" + idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" + iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" + ) + for i := 0; i < 4; i++ { + var b []byte + b = append(b, pngHeader...) + b = append(b, ihdr...) + b = append(b, plte...) + for j := 0; j < i; j++ { + b = append(b, trns...) + } + b = append(b, idat...) + b = append(b, iend...) + + var want color.Color + m, err := Decode(bytes.NewReader(b)) + switch i { + case 0: + if err != nil { + t.Errorf("%d tRNS chunks: %v", i, err) + continue + } + want = color.RGBA{0xff, 0x00, 0x00, 0xff} + case 1: + if err != nil { + t.Errorf("%d tRNS chunks: %v", i, err) + continue + } + want = color.NRGBA{0xff, 0x00, 0x00, 0x7f} + default: + if err == nil { + t.Errorf("%d tRNS chunks: got nil error, want non-nil", i) + } + continue + } + if got := m.At(0, 0); got != want { + t.Errorf("%d tRNS chunks: got %T %v, want %T %v", i, got, got, want, want) + } + } +} + func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) { b.StopTimer() data, err := ioutil.ReadFile(filename) diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/testdata/benchRGB-interlace.png b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/benchRGB-interlace.png Binary files differnew file mode 100644 index 00000000000..b4b5daba3c6 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/benchRGB-interlace.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/testdata/gray-gradient.interlaced.png b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/gray-gradient.interlaced.png Binary files differnew file mode 100644 index 00000000000..01f657ae86d --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/gray-gradient.interlaced.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/testdata/gray-gradient.png b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/gray-gradient.png Binary files differnew file mode 100644 index 00000000000..6de1cd36f4a --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/gray-gradient.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/testdata/pngsuite/basn3p04-31i.png b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/pngsuite/basn3p04-31i.png Binary files differnew file mode 100644 index 00000000000..540137cb556 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/pngsuite/basn3p04-31i.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/png/testdata/pngsuite/basn3p04-31i.sng b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/pngsuite/basn3p04-31i.sng new file mode 100644 index 00000000000..31b87c72d12 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/png/testdata/pngsuite/basn3p04-31i.sng @@ -0,0 +1,57 @@ +#SNG: from basn3p04-31i.png +IHDR { + width: 31; height: 31; bitdepth: 4; + using color palette; +} +gAMA {1.0000} +PLTE { + ( 34, 0,255) # rgb = (0x22,0x00,0xff) + ( 0,255,255) # rgb = (0x00,0xff,0xff) + (136, 0,255) # rgb = (0x88,0x00,0xff) + ( 34,255, 0) # rgb = (0x22,0xff,0x00) + ( 0,153,255) # rgb = (0x00,0x99,0xff) + (255,102, 0) # rgb = (0xff,0x66,0x00) + (221, 0,255) # rgb = (0xdd,0x00,0xff) + (119,255, 0) # rgb = (0x77,0xff,0x00) + (255, 0, 0) # rgb = (0xff,0x00,0x00) + ( 0,255,153) # rgb = (0x00,0xff,0x99) + (221,255, 0) # rgb = (0xdd,0xff,0x00) + (255, 0,187) # rgb = (0xff,0x00,0xbb) + (255,187, 0) # rgb = (0xff,0xbb,0x00) + ( 0, 68,255) # rgb = (0x00,0x44,0xff) + ( 0,255, 68) # rgb = (0x00,0xff,0x44) +} +IMAGE { + pixels hex +88885555ccccaaaa77773333eeee9990 +88885555ccccaaaa77773333eeee9990 +88885555ccccaaaa77773333eeee9990 +88885555ccccaaaa77773333eeee9990 +5555ccccaaaa77773333eeee99991110 +5555ccccaaaa77773333eeee99991110 +5555ccccaaaa77773333eeee99991110 +5555ccccaaaa77773333eeee99991110 +ccccaaaa77773333eeee999911114440 +ccccaaaa77773333eeee999911114440 +ccccaaaa77773333eeee999911114440 +ccccaaaa77773333eeee999911114440 +aaaa77773333eeee999911114444ddd0 +aaaa77773333eeee999911114444ddd0 +aaaa77773333eeee999911114444ddd0 +aaaa77773333eeee999911114444ddd0 +77773333eeee999911114444dddd0000 +77773333eeee999911114444dddd0000 +77773333eeee999911114444dddd0000 +77773333eeee999911114444dddd0000 +3333eeee999911114444dddd00002220 +3333eeee999911114444dddd00002220 +3333eeee999911114444dddd00002220 +3333eeee999911114444dddd00002220 +eeee999911114444dddd000022226660 +eeee999911114444dddd000022226660 +eeee999911114444dddd000022226660 +eeee999911114444dddd000022226660 +999911114444dddd000022226666bbb0 +999911114444dddd000022226666bbb0 +999911114444dddd000022226666bbb0 +} diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.221212.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.221212.jpeg Binary files differnew file mode 100644 index 00000000000..f069c76af9c --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.221212.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.221212.png b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.221212.png Binary files differnew file mode 100644 index 00000000000..d619a6286bc --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.221212.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.cmyk.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.cmyk.jpeg Binary files differnew file mode 100644 index 00000000000..507df843b5a --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.cmyk.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.cmyk.png b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.cmyk.png Binary files differnew file mode 100644 index 00000000000..ef7b2b88d87 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.cmyk.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.410.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.410.jpeg Binary files differnew file mode 100644 index 00000000000..4cebd1eb25c --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.410.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.410.progressive.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.410.progressive.jpeg Binary files differnew file mode 100644 index 00000000000..fb7140217e2 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.410.progressive.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.411.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.411.jpeg Binary files differnew file mode 100644 index 00000000000..b90de18721d --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.411.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.411.progressive.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.411.progressive.jpeg Binary files differnew file mode 100644 index 00000000000..1ddb22b8b0e --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.q50.411.progressive.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.rgb.jpeg b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.rgb.jpeg Binary files differnew file mode 100644 index 00000000000..fc2ce3ca91f --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.rgb.jpeg diff --git a/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.rgb.png b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.rgb.png Binary files differnew file mode 100644 index 00000000000..edb716d3415 --- /dev/null +++ b/llgo/third_party/gofrontend/libgo/go/image/testdata/video-001.rgb.png diff --git a/llgo/third_party/gofrontend/libgo/go/image/ycbcr.go b/llgo/third_party/gofrontend/libgo/go/image/ycbcr.go index 7c773f2f0a4..93c354b33b4 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/ycbcr.go +++ b/llgo/third_party/gofrontend/libgo/go/image/ycbcr.go @@ -16,6 +16,8 @@ const ( YCbCrSubsampleRatio422 YCbCrSubsampleRatio420 YCbCrSubsampleRatio440 + YCbCrSubsampleRatio411 + YCbCrSubsampleRatio410 ) func (s YCbCrSubsampleRatio) String() string { @@ -28,6 +30,10 @@ func (s YCbCrSubsampleRatio) String() string { return "YCbCrSubsampleRatio420" case YCbCrSubsampleRatio440: return "YCbCrSubsampleRatio440" + case YCbCrSubsampleRatio411: + return "YCbCrSubsampleRatio411" + case YCbCrSubsampleRatio410: + return "YCbCrSubsampleRatio410" } return "YCbCrSubsampleRatioUnknown" } @@ -43,6 +49,8 @@ func (s YCbCrSubsampleRatio) String() string { // 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. +// For 4:1:1, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/4. +// For 4:1:0, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/8. type YCbCr struct { Y, Cb, Cr []uint8 YStride int @@ -92,6 +100,10 @@ func (p *YCbCr) COffset(x, y int) int { 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) + case YCbCrSubsampleRatio411: + return (y-p.Rect.Min.Y)*p.CStride + (x/4 - p.Rect.Min.X/4) + case YCbCrSubsampleRatio410: + return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/4 - p.Rect.Min.X/4) } // Default to 4:4:4 subsampling. return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X) @@ -139,16 +151,25 @@ func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { case YCbCrSubsampleRatio440: cw = w ch = (r.Max.Y+1)/2 - r.Min.Y/2 + case YCbCrSubsampleRatio411: + cw = (r.Max.X+3)/4 - r.Min.X/4 + ch = h + case YCbCrSubsampleRatio410: + cw = (r.Max.X+3)/4 - r.Min.X/4 + ch = (r.Max.Y+1)/2 - r.Min.Y/2 default: // Default to 4:4:4 subsampling. cw = w ch = h } - b := make([]byte, w*h+2*cw*ch) + i0 := w*h + 0*cw*ch + i1 := w*h + 1*cw*ch + i2 := w*h + 2*cw*ch + b := make([]byte, i2) return &YCbCr{ - Y: b[:w*h], - Cb: b[w*h+0*cw*ch : w*h+1*cw*ch], - Cr: b[w*h+1*cw*ch : w*h+2*cw*ch], + Y: b[:i0:i0], + Cb: b[i0:i1:i1], + Cr: b[i1:i2:i2], SubsampleRatio: subsampleRatio, YStride: w, CStride: cw, diff --git a/llgo/third_party/gofrontend/libgo/go/image/ycbcr_test.go b/llgo/third_party/gofrontend/libgo/go/image/ycbcr_test.go index a5f4482654f..4996bc8dcae 100644 --- a/llgo/third_party/gofrontend/libgo/go/image/ycbcr_test.go +++ b/llgo/third_party/gofrontend/libgo/go/image/ycbcr_test.go @@ -37,6 +37,8 @@ func TestYCbCr(t *testing.T) { YCbCrSubsampleRatio422, YCbCrSubsampleRatio420, YCbCrSubsampleRatio440, + YCbCrSubsampleRatio411, + YCbCrSubsampleRatio410, } deltas := []Point{ Pt(0, 0), @@ -105,3 +107,27 @@ func testYCbCr(t *testing.T, r Rectangle, subsampleRatio YCbCrSubsampleRatio, de } } } + +func TestYCbCrSlicesDontOverlap(t *testing.T) { + m := NewYCbCr(Rect(0, 0, 8, 8), YCbCrSubsampleRatio420) + names := []string{"Y", "Cb", "Cr"} + slices := [][]byte{ + m.Y[:cap(m.Y)], + m.Cb[:cap(m.Cb)], + m.Cr[:cap(m.Cr)], + } + for i, slice := range slices { + want := uint8(10 + i) + for j := range slice { + slice[j] = want + } + } + for i, slice := range slices { + want := uint8(10 + i) + for j, got := range slice { + if got != want { + t.Fatalf("m.%s[%d]: got %d, want %d", names[i], j, got, want) + } + } + } +} |

