Commit 237ee392 authored by Nigel Tao's avatar Nigel Tao

image/png: optimize encoding image.Gray and image.NRGBA images.

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
parent e7d7ea21
...@@ -290,26 +290,42 @@ func writeImage(w io.Writer, m image.Image, cb int) error { ...@@ -290,26 +290,42 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
} }
pr := make([]uint8, 1+bpp*b.Dx()) 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++ { for y := b.Min.Y; y < b.Max.Y; y++ {
// Convert from colors to bytes. // Convert from colors to bytes.
i := 1 i := 1
switch cb { switch cb {
case cbG8: case cbG8:
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++ { for x := b.Min.X; x < b.Max.X; x++ {
c := color.GrayModel.Convert(m.At(x, y)).(color.Gray) c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
cr[0][i] = c.Y cr[0][i] = c.Y
i++ i++
} }
}
case cbTC8: case cbTC8:
// We have previously verified that the alpha value is fully opaque. // We have previously verified that the alpha value is fully opaque.
cr0 := cr[0] cr0 := cr[0]
if rgba, _ := m.(*image.RGBA); rgba != nil { stride, pix := 0, []byte(nil)
j0 := (y - b.Min.Y) * rgba.Stride 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 j1 := j0 + b.Dx()*4
for j := j0; j < j1; j += 4 { for j := j0; j < j1; j += 4 {
cr0[i+0] = rgba.Pix[j+0] cr0[i+0] = pix[j+0]
cr0[i+1] = rgba.Pix[j+1] cr0[i+1] = pix[j+1]
cr0[i+2] = rgba.Pix[j+2] cr0[i+2] = pix[j+2]
i += 3 i += 3
} }
} else { } else {
...@@ -322,9 +338,9 @@ func writeImage(w io.Writer, m image.Image, cb int) error { ...@@ -322,9 +338,9 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
} }
} }
case cbP8: case cbP8:
if p, _ := m.(*image.Paletted); p != nil { if paletted != nil {
offset := (y - b.Min.Y) * p.Stride offset := (y - b.Min.Y) * paletted.Stride
copy(cr[0][1:], p.Pix[offset:offset+b.Dx()]) copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
} else { } else {
pi := m.(image.PalettedImage) pi := m.(image.PalettedImage)
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
...@@ -333,6 +349,10 @@ func writeImage(w io.Writer, m image.Image, cb int) error { ...@@ -333,6 +349,10 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
} }
} }
case cbTCA8: case cbTCA8:
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. // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA) c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
...@@ -342,6 +362,7 @@ func writeImage(w io.Writer, m image.Image, cb int) error { ...@@ -342,6 +362,7 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
cr[0][i+3] = c.A cr[0][i+3] = c.A
i += 4 i += 4
} }
}
case cbG16: case cbG16:
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16) c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16)
......
...@@ -101,6 +101,49 @@ func TestSubImage(t *testing.T) { ...@@ -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) { func BenchmarkEncodePaletted(b *testing.B) {
b.StopTimer() b.StopTimer()
img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{ img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{
...@@ -138,7 +181,7 @@ func BenchmarkEncodeRGBA(b *testing.B) { ...@@ -138,7 +181,7 @@ func BenchmarkEncodeRGBA(b *testing.B) {
b.StopTimer() b.StopTimer()
img := image.NewRGBA(image.Rect(0, 0, 640, 480)) img := image.NewRGBA(image.Rect(0, 0, 640, 480))
if img.Opaque() { 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.SetBytes(640 * 480 * 4)
b.StartTimer() b.StartTimer()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment