From: Nigel Tao Date: Sat, 25 Apr 2020 12:29:02 +0000 (+1000) Subject: image/draw: optimize paletted dst + uniform src X-Git-Tag: go1.15beta1~337 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=42c48998aada0df10279650d04a018c83cbfa518;p=gostls13.git image/draw: optimize paletted dst + uniform src name old time/op new time/op delta PalettedFill-4 5.74ms ± 1% 0.01ms ± 1% -99.78% (p=0.008 n=5+5) PalettedRGBA-4 3.34ms ± 3% 3.33ms ± 0% ~ (p=0.690 n=5+5) Fixes #35938 Thanks to pjbgtnj for the suggestion. Change-Id: I07b494482cce918f556e196c5a4b481b4c16de3a Reviewed-on: https://go-review.googlesource.com/c/go/+/230118 Run-TryBot: Nigel Tao TryBot-Result: Gobot Gobot Reviewed-by: Rob Pike --- diff --git a/src/image/draw/bench_test.go b/src/image/draw/bench_test.go index a41d7e7dfb..831fd958ba 100644 --- a/src/image/draw/bench_test.go +++ b/src/image/draw/bench_test.go @@ -236,7 +236,11 @@ func BenchmarkRGBA(b *testing.B) { bench(b, color.RGBAModel, color.RGBA64Model, nil, Src) } -func BenchmarkPaletted(b *testing.B) { +func BenchmarkPalettedFill(b *testing.B) { + bench(b, palette, nil, nil, Src) +} + +func BenchmarkPalettedRGBA(b *testing.B) { bench(b, palette, color.RGBAModel, nil, Src) } diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go index 932a544483..8f96aa2d18 100644 --- a/src/image/draw/draw.go +++ b/src/image/draw/draw.go @@ -180,9 +180,25 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas drawRGBA(dst0, r, src, sp, mask, mp, op) return case *image.Paletted: - if op == Src && mask == nil && !processBackward(dst, r, src, sp) { - drawPaletted(dst0, r, src, sp, false) - return + if op == Src && mask == nil { + if src0, ok := src.(*image.Uniform); ok { + colorIndex := uint8(dst0.Palette.Index(src0.C)) + i0 := dst0.PixOffset(r.Min.X, r.Min.Y) + i1 := i0 + r.Dx() + for i := i0; i < i1; i++ { + dst0.Pix[i] = colorIndex + } + firstRow := dst0.Pix[i0:i1] + for y := r.Min.Y + 1; y < r.Max.Y; y++ { + i0 += dst0.Stride + i1 += dst0.Stride + copy(dst0.Pix[i0:i1], firstRow) + } + return + } else if !processBackward(dst, r, src, sp) { + drawPaletted(dst0, r, src, sp, false) + return + } } } diff --git a/src/image/draw/draw_test.go b/src/image/draw/draw_test.go index dea51b6bc5..9c5a118400 100644 --- a/src/image/draw/draw_test.go +++ b/src/image/draw/draw_test.go @@ -434,11 +434,11 @@ func TestPaletted(t *testing.T) { t.Fatalf("open: %v", err) } defer f.Close() - src, err := png.Decode(f) + video001, err := png.Decode(f) if err != nil { t.Fatalf("decode: %v", err) } - b := src.Bounds() + b := video001.Bounds() cgaPalette := color.Palette{ color.RGBA{0x00, 0x00, 0x00, 0xff}, @@ -450,19 +450,25 @@ func TestPaletted(t *testing.T) { "src": Src, "floyd-steinberg": FloydSteinberg, } + sources := map[string]image.Image{ + "uniform": &image.Uniform{color.RGBA{0xff, 0x7f, 0xff, 0xff}}, + "video001": video001, + } -loop: for dName, d := range drawers { - dst0 := image.NewPaletted(b, cgaPalette) - dst1 := image.NewPaletted(b, cgaPalette) - d.Draw(dst0, b, src, image.Point{}) - d.Draw(embeddedPaletted{dst1}, b, src, image.Point{}) - for y := b.Min.Y; y < b.Max.Y; y++ { - for x := b.Min.X; x < b.Max.X; x++ { - if !eq(dst0.At(x, y), dst1.At(x, y)) { - t.Errorf("%s: at (%d, %d), %v versus %v", - dName, x, y, dst0.At(x, y), dst1.At(x, y)) - continue loop + loop: + for sName, src := range sources { + dst0 := image.NewPaletted(b, cgaPalette) + dst1 := image.NewPaletted(b, cgaPalette) + d.Draw(dst0, b, src, image.Point{}) + d.Draw(embeddedPaletted{dst1}, b, src, image.Point{}) + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + if !eq(dst0.At(x, y), dst1.At(x, y)) { + t.Errorf("%s / %s: at (%d, %d), %v versus %v", + dName, sName, x, y, dst0.At(x, y), dst1.At(x, y)) + continue loop + } } } }