mirror of https://github.com/golang/go.git
image/color: optimize RGBToYCbCr
Apply optimizations used to speed up YCbCrToRGB from https://go-review.googlesource.com/#/c/21910/ to RGBToYCbCr. name old time/op new time/op delta RGBToYCbCr/0-2 6.81ns ± 0% 5.96ns ± 0% -12.48% (p=0.000 n=38+50) RGBToYCbCr/Cb-2 7.68ns ± 0% 6.13ns ± 0% -20.21% (p=0.000 n=50+33) RGBToYCbCr/Cr-2 6.84ns ± 0% 6.04ns ± 0% -11.70% (p=0.000 n=39+42) Updates #15260 Change-Id: If3ea5393ae371a955ddf18ab226aae20b48f9692 Reviewed-on: https://go-review.googlesource.com/22411 Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ralph Corderoy <ralph@inputplus.co.uk>
This commit is contained in:
parent
8f2e780e8a
commit
102cf2ae03
|
|
@ -15,24 +15,37 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
|
|||
r1 := int32(r)
|
||||
g1 := int32(g)
|
||||
b1 := int32(b)
|
||||
|
||||
// yy is in range [0,0xff].
|
||||
yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
|
||||
cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
|
||||
cr := (32768*r1 - 27440*g1 - 5328*b1 + 257<<15) >> 16
|
||||
if yy < 0 {
|
||||
yy = 0
|
||||
} else if yy > 0xff {
|
||||
yy = 0xff
|
||||
|
||||
// The bit twiddling below is equivalent to
|
||||
//
|
||||
// cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
|
||||
// if cb < 0 {
|
||||
// cb = 0
|
||||
// } else if cb > 0xff {
|
||||
// cb = ^int32(0)
|
||||
// }
|
||||
//
|
||||
// but uses fewer branches and is faster.
|
||||
// Note that the uint8 type conversion in the return
|
||||
// statement will convert ^int32(0) to 0xff.
|
||||
// The code below to compute cr uses a similar pattern.
|
||||
cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
|
||||
if uint32(cb)&0xff000000 == 0 {
|
||||
cb >>= 16
|
||||
} else {
|
||||
cb = ^(cb >> 31)
|
||||
}
|
||||
if cb < 0 {
|
||||
cb = 0
|
||||
} else if cb > 0xff {
|
||||
cb = 0xff
|
||||
}
|
||||
if cr < 0 {
|
||||
cr = 0
|
||||
} else if cr > 0xff {
|
||||
cr = 0xff
|
||||
|
||||
cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
|
||||
if uint32(cr)&0xff000000 == 0 {
|
||||
cr >>= 16
|
||||
} else {
|
||||
cr = ^(cr >> 31)
|
||||
}
|
||||
|
||||
return uint8(yy), uint8(cb), uint8(cr)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ func TestPalette(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var sinkr, sinkg, sinkb uint8
|
||||
var sink uint8
|
||||
|
||||
func BenchmarkYCbCrToRGB(b *testing.B) {
|
||||
// YCbCrToRGB does saturating arithmetic.
|
||||
|
|
@ -180,17 +180,38 @@ func BenchmarkYCbCrToRGB(b *testing.B) {
|
|||
// different paths through the generated code.
|
||||
b.Run("0", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sinkr, sinkg, sinkb = YCbCrToRGB(0, 0, 0)
|
||||
sink, sink, sink = YCbCrToRGB(0, 0, 0)
|
||||
}
|
||||
})
|
||||
b.Run("128", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sinkr, sinkg, sinkb = YCbCrToRGB(128, 128, 128)
|
||||
sink, sink, sink = YCbCrToRGB(128, 128, 128)
|
||||
}
|
||||
})
|
||||
b.Run("255", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sinkr, sinkg, sinkb = YCbCrToRGB(255, 255, 255)
|
||||
sink, sink, sink = YCbCrToRGB(255, 255, 255)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkRGBToYCbCr(b *testing.B) {
|
||||
// RGBToYCbCr does saturating arithmetic.
|
||||
// Different values can take different paths
|
||||
// through the generated code.
|
||||
b.Run("0", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink, sink, sink = RGBToYCbCr(0, 0, 0)
|
||||
}
|
||||
})
|
||||
b.Run("Cb", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink, sink, sink = RGBToYCbCr(0, 0, 255)
|
||||
}
|
||||
})
|
||||
b.Run("Cr", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink, sink, sink = RGBToYCbCr(255, 0, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue