]> Cypherpunks repositories - gostls13.git/commitdiff
image/draw: add a fast path for Gray src images.
authorNigel Tao <nigeltao@golang.org>
Sat, 28 Feb 2015 04:46:01 +0000 (15:46 +1100)
committerNigel Tao <nigeltao@golang.org>
Sat, 28 Feb 2015 21:43:39 +0000 (21:43 +0000)
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 <r@golang.org>
src/image/draw/bench_test.go
src/image/draw/draw.go
src/image/draw/draw_test.go

index cc62e25f1bbfcedfdacca70f464aa9796c2d24b0..51145d11270c903a7d598b6be47f1713243d2f62 100644 (file)
@@ -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)
 }
index 649634114449c56ad1e810d6e60a5eb0a5a8f6ff..f704ff00df650b5ac18fe05e9d0bc5557fd4015c 100644 (file)
@@ -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.
index 8b4ca029ebbd2ba70a3084e9c03ab84986282d22..a58f0f49849048f9d5e08e5e0953b65a3cb61c5d 100644 (file)
@@ -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}.