1
0
mirror of https://github.com/golang/go synced 2024-11-25 10:07:56 -07:00

image/png: optimize the paeth filter implementation.

image/png benchmarks:
benchmark                       old ns/op    new ns/op    delta
BenchmarkPaeth                         10            7  -29.21%
BenchmarkDecodeGray               2381745      2241620   -5.88%
BenchmarkDecodeNRGBAGradient      9535555      8835100   -7.35%
BenchmarkDecodeNRGBAOpaque        8189590      7611865   -7.05%
BenchmarkDecodePaletted           1300688      1301940   +0.10%
BenchmarkDecodeRGB                6760146      6317082   -6.55%
BenchmarkEncodePaletted           6048596      6122666   +1.22%
BenchmarkEncodeRGBOpaque         18891140     19474230   +3.09%
BenchmarkEncodeRGBA              78945350     78552600   -0.50%

Wall time for Denis Cheremisov's PNG-decoding program given in
https://groups.google.com/group/golang-nuts/browse_thread/thread/22aa8a05040fdd49
Before: 2.25s
After:  2.27s
Delta:  +1%

The same program, but with a different PNG input file
(http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png)
and only 100 iterations instead of 1000
Before: 4.78s
After:  4.42s
Delta:  -8%

R=rsc
CC=golang-dev
https://golang.org/cl/6242056
This commit is contained in:
Nigel Tao 2012-05-25 14:08:51 +10:00
parent 97cbf47c78
commit 1423ecb126
2 changed files with 72 additions and 12 deletions

View File

@ -0,0 +1,51 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package png
import (
"testing"
)
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
// slowPaeth is a slow but simple implementation of the Paeth function.
// It is a straight port of the sample code in the PNG spec, section 9.4.
func slowPaeth(a, b, c uint8) uint8 {
p := int(a) + int(b) - int(c)
pa := abs(p - int(a))
pb := abs(p - int(b))
pc := abs(p - int(c))
if pa <= pb && pa <= pc {
return a
} else if pb <= pc {
return b
}
return c
}
func TestPaeth(t *testing.T) {
for a := 0; a < 256; a += 15 {
for b := 0; b < 256; b += 15 {
for c := 0; c < 256; c += 15 {
got := paeth(uint8(a), uint8(b), uint8(c))
want := slowPaeth(uint8(a), uint8(b), uint8(c))
if got != want {
t.Errorf("a, b, c = %d, %d, %d: got %d, want %d", a, b, c, got, want)
}
}
}
}
}
func BenchmarkPaeth(b *testing.B) {
for i := 0; i < b.N; i++ {
paeth(uint8(i>>16), uint8(i>>8), uint8(i))
}
}

View File

@ -98,13 +98,6 @@ type UnsupportedError string
func (e UnsupportedError) Error() string { return "png: unsupported feature: " + string(e) }
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func min(a, b int) int {
if a < b {
return a
@ -241,12 +234,28 @@ func (d *decoder) parsetRNS(length uint32) error {
return d.verifyChecksum()
}
// The Paeth filter function, as per the PNG specification.
// paeth implements the Paeth filter function, as per the PNG specification.
func paeth(a, b, c uint8) uint8 {
p := int(a) + int(b) - int(c)
pa := abs(p - int(a))
pb := abs(p - int(b))
pc := abs(p - int(c))
// This is an optimized version of the sample code in the PNG spec.
// For example, the sample code starts with:
// p := int(a) + int(b) - int(c)
// pa := abs(p - int(a))
// but the optimized form uses fewer arithmetic operations:
// pa := int(b) - int(c)
// pa = abs(pa)
pc := int(c)
pa := int(b) - pc
pb := int(a) - pc
pc = pa + pb
if pa < 0 {
pa = -pa
}
if pb < 0 {
pb = -pb
}
if pc < 0 {
pc = -pc
}
if pa <= pb && pa <= pc {
return a
} else if pb <= pc {