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: 's avatarJosh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRalph Corderoy <ralph@inputplus.co.uk>
parent 8f2e780e
......@@ -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
}
if cb < 0 {
cb = 0
} else if cb > 0xff {
cb = 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 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)
}
})
}
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