Commit 102cf2ae authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Martin Möhrmann

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/22411Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRalph Corderoy <ralph@inputplus.co.uk>
parent 8f2e780e
...@@ -15,24 +15,37 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) { ...@@ -15,24 +15,37 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
r1 := int32(r) r1 := int32(r)
g1 := int32(g) g1 := int32(g)
b1 := int32(b) b1 := int32(b)
// yy is in range [0,0xff].
yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16 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 // The bit twiddling below is equivalent to
if yy < 0 { //
yy = 0 // cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
} else if yy > 0xff { // if cb < 0 {
yy = 0xff // cb = 0
} // } else if cb > 0xff {
if cb < 0 { // cb = ^int32(0)
cb = 0 // }
} else if cb > 0xff { //
cb = 0xff // 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 cr < 0 {
cr = 0 cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
} else if cr > 0xff { if uint32(cr)&0xff000000 == 0 {
cr = 0xff cr >>= 16
} else {
cr = ^(cr >> 31)
} }
return uint8(yy), uint8(cb), uint8(cr) return uint8(yy), uint8(cb), uint8(cr)
} }
......
...@@ -172,7 +172,7 @@ func TestPalette(t *testing.T) { ...@@ -172,7 +172,7 @@ func TestPalette(t *testing.T) {
} }
} }
var sinkr, sinkg, sinkb uint8 var sink uint8
func BenchmarkYCbCrToRGB(b *testing.B) { func BenchmarkYCbCrToRGB(b *testing.B) {
// YCbCrToRGB does saturating arithmetic. // YCbCrToRGB does saturating arithmetic.
...@@ -180,17 +180,38 @@ func BenchmarkYCbCrToRGB(b *testing.B) { ...@@ -180,17 +180,38 @@ func BenchmarkYCbCrToRGB(b *testing.B) {
// different paths through the generated code. // different paths through the generated code.
b.Run("0", func(b *testing.B) { b.Run("0", func(b *testing.B) {
for i := 0; i < b.N; i++ { 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) { b.Run("128", func(b *testing.B) {
for i := 0; i < b.N; i++ { 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) { b.Run("255", func(b *testing.B) {
for i := 0; i < b.N; i++ { 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)
} }
}) })
} }
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