]> Cypherpunks repositories - gostls13.git/commitdiff
image/png: optimize encoding image.Gray and image.NRGBA images.
authorNigel Tao <nigeltao@golang.org>
Thu, 13 Sep 2012 05:47:12 +0000 (15:47 +1000)
committerNigel Tao <nigeltao@golang.org>
Thu, 13 Sep 2012 05:47:12 +0000 (15:47 +1000)
benchmark                    old ns/op    new ns/op    delta
BenchmarkEncodeGray           23616080      5624558  -76.18%
BenchmarkEncodeNRGBOpaque     34181260     17144380  -49.84%
BenchmarkEncodeNRGBA          41235820     20345990  -50.66%
BenchmarkEncodePaletted        5594652      5620362   +0.46%
BenchmarkEncodeRGBOpaque      17242210     17168820   -0.43%
BenchmarkEncodeRGBA           66515720     67243560   +1.09%

R=r
CC=golang-dev
https://golang.org/cl/6490099

src/pkg/image/png/writer.go
src/pkg/image/png/writer_test.go

index 88683a937b4e291139d6db54d3b56b436be3c485..093d47193ba50d351733bab1118c14d7c0934c31 100644 (file)
@@ -290,26 +290,42 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
        }
        pr := make([]uint8, 1+bpp*b.Dx())
 
+       gray, _ := m.(*image.Gray)
+       rgba, _ := m.(*image.RGBA)
+       paletted, _ := m.(*image.Paletted)
+       nrgba, _ := m.(*image.NRGBA)
+
        for y := b.Min.Y; y < b.Max.Y; y++ {
                // Convert from colors to bytes.
                i := 1
                switch cb {
                case cbG8:
-                       for x := b.Min.X; x < b.Max.X; x++ {
-                               c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
-                               cr[0][i] = c.Y
-                               i++
+                       if gray != nil {
+                               offset := (y - b.Min.Y) * gray.Stride
+                               copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
+                       } else {
+                               for x := b.Min.X; x < b.Max.X; x++ {
+                                       c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
+                                       cr[0][i] = c.Y
+                                       i++
+                               }
                        }
                case cbTC8:
                        // We have previously verified that the alpha value is fully opaque.
                        cr0 := cr[0]
-                       if rgba, _ := m.(*image.RGBA); rgba != nil {
-                               j0 := (y - b.Min.Y) * rgba.Stride
+                       stride, pix := 0, []byte(nil)
+                       if rgba != nil {
+                               stride, pix = rgba.Stride, rgba.Pix
+                       } else if nrgba != nil {
+                               stride, pix = nrgba.Stride, nrgba.Pix
+                       }
+                       if stride != 0 {
+                               j0 := (y - b.Min.Y) * stride
                                j1 := j0 + b.Dx()*4
                                for j := j0; j < j1; j += 4 {
-                                       cr0[i+0] = rgba.Pix[j+0]
-                                       cr0[i+1] = rgba.Pix[j+1]
-                                       cr0[i+2] = rgba.Pix[j+2]
+                                       cr0[i+0] = pix[j+0]
+                                       cr0[i+1] = pix[j+1]
+                                       cr0[i+2] = pix[j+2]
                                        i += 3
                                }
                        } else {
@@ -322,9 +338,9 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
                                }
                        }
                case cbP8:
-                       if p, _ := m.(*image.Paletted); p != nil {
-                               offset := (y - b.Min.Y) * p.Stride
-                               copy(cr[0][1:], p.Pix[offset:offset+b.Dx()])
+                       if paletted != nil {
+                               offset := (y - b.Min.Y) * paletted.Stride
+                               copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
                        } else {
                                pi := m.(image.PalettedImage)
                                for x := b.Min.X; x < b.Max.X; x++ {
@@ -333,14 +349,19 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
                                }
                        }
                case cbTCA8:
-                       // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
-                       for x := b.Min.X; x < b.Max.X; x++ {
-                               c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
-                               cr[0][i+0] = c.R
-                               cr[0][i+1] = c.G
-                               cr[0][i+2] = c.B
-                               cr[0][i+3] = c.A
-                               i += 4
+                       if nrgba != nil {
+                               offset := (y - b.Min.Y) * nrgba.Stride
+                               copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
+                       } else {
+                               // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
+                               for x := b.Min.X; x < b.Max.X; x++ {
+                                       c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
+                                       cr[0][i+0] = c.R
+                                       cr[0][i+1] = c.G
+                                       cr[0][i+2] = c.B
+                                       cr[0][i+3] = c.A
+                                       i += 4
+                               }
                        }
                case cbG16:
                        for x := b.Min.X; x < b.Max.X; x++ {
index 644c4fb44b39a6a9fb4732213fae53f32dcf2d6a..3116fc9ff94e467612083d0cd8e1c9a2e3cc6121 100644 (file)
@@ -101,6 +101,49 @@ func TestSubImage(t *testing.T) {
        }
 }
 
+func BenchmarkEncodeGray(b *testing.B) {
+       b.StopTimer()
+       img := image.NewGray(image.Rect(0, 0, 640, 480))
+       b.SetBytes(640 * 480 * 1)
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               Encode(ioutil.Discard, img)
+       }
+}
+
+func BenchmarkEncodeNRGBOpaque(b *testing.B) {
+       b.StopTimer()
+       img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
+       // Set all pixels to 0xFF alpha to force opaque mode.
+       bo := img.Bounds()
+       for y := bo.Min.Y; y < bo.Max.Y; y++ {
+               for x := bo.Min.X; x < bo.Max.X; x++ {
+                       img.Set(x, y, color.NRGBA{0, 0, 0, 255})
+               }
+       }
+       if !img.Opaque() {
+               b.Fatal("expected image to be opaque")
+       }
+       b.SetBytes(640 * 480 * 4)
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               Encode(ioutil.Discard, img)
+       }
+}
+
+func BenchmarkEncodeNRGBA(b *testing.B) {
+       b.StopTimer()
+       img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
+       if img.Opaque() {
+               b.Fatal("expected image not to be opaque")
+       }
+       b.SetBytes(640 * 480 * 4)
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               Encode(ioutil.Discard, img)
+       }
+}
+
 func BenchmarkEncodePaletted(b *testing.B) {
        b.StopTimer()
        img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{
@@ -138,7 +181,7 @@ func BenchmarkEncodeRGBA(b *testing.B) {
        b.StopTimer()
        img := image.NewRGBA(image.Rect(0, 0, 640, 480))
        if img.Opaque() {
-               b.Fatal("expected image to not be opaque")
+               b.Fatal("expected image not to be opaque")
        }
        b.SetBytes(640 * 480 * 4)
        b.StartTimer()