From: Nigel Tao Date: Sat, 28 Feb 2015 04:46:01 +0000 (+1100) Subject: image/draw: add a fast path for Gray src images. X-Git-Tag: go1.5beta1~1813 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c20323d2bb44146c49f71f583a1f4a089360d062;p=gostls13.git image/draw: add a fast path for Gray src images. Grayscale PNG and JPEG images are not uncommon. We should have a fast path. Also add a benchmark for the recently added CMYK fast path. benchmark old ns/op new ns/op delta BenchmarkGray 13960348 324152 -97.68% Change-Id: I72b5838c8c3d1f2d0a4536a848e020e80b10c0f7 Reviewed-on: https://go-review.googlesource.com/6237 Reviewed-by: Rob Pike --- diff --git a/src/image/draw/bench_test.go b/src/image/draw/bench_test.go index cc62e25f1b..51145d1127 100644 --- a/src/image/draw/bench_test.go +++ b/src/image/draw/bench_test.go @@ -57,6 +57,29 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) { 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 +202,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) } diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go index 6496341144..f704ff00df 100644 --- a/src/image/draw/draw.go +++ b/src/image/draw/draw.go @@ -127,6 +127,9 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas if drawYCbCr(dst0, r, src0, sp) { return } + case *image.Gray: + drawGray(dst0, r, src0, sp) + return case *image.CMYK: drawCMYK(dst0, r, src0, sp) return @@ -154,6 +157,9 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas if drawYCbCr(dst0, r, src0, sp) { return } + case *image.Gray: + drawGray(dst0, r, src0, sp) + return case *image.CMYK: drawCMYK(dst0, r, src0, sp) return @@ -472,6 +478,30 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po return true } +func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point) { + // An image.Gray is always fully opaque, and so if the mask is implicitly nil + // (i.e. fully opaque) then the op is effectively always Src. + 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 + } + } +} + func drawCMYK(dst *image.RGBA, r image.Rectangle, src *image.CMYK, sp image.Point) { // An image.CMYK is always fully opaque, and so if the mask is implicitly nil // (i.e. fully opaque) then the op is effectively always Src. diff --git a/src/image/draw/draw_test.go b/src/image/draw/draw_test.go index 8b4ca029eb..a58f0f4984 100644 --- a/src/image/draw/draw_test.go +++ b/src/image/draw/draw_test.go @@ -74,6 +74,16 @@ 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++ { @@ -157,6 +167,16 @@ 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}.