]> Cypherpunks repositories - gostls13.git/commitdiff
image/color: optimize RGBToYCbCr
authorMartin Möhrmann <martisch@uos.de>
Fri, 15 Apr 2016 07:38:36 +0000 (09:38 +0200)
committerMartin Möhrmann <martisch@uos.de>
Wed, 27 Apr 2016 06:19:37 +0000 (06:19 +0000)
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>
src/image/color/ycbcr.go
src/image/color/ycbcr_test.go

index 55549c3fcee24275942b0312afc40db16f1b6d4a..3df5d3675d6ef8231cdd2a30b18ea7848e0bd01e 100644 (file)
@@ -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)
 }
 
index 1b110691a24d27c8271b4d588b12ac7c99a169a5..561699f4e0eaaf325d60efe9194bfcd4d040cab4 100644 (file)
@@ -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)
                }
        })
 }