aboutsummaryrefslogtreecommitdiff
path: root/src/image
diff options
context:
space:
mode:
authorIan Davis <nospam@iandavis.com>2018-09-24 10:20:46 +0100
committerBrad Fitzpatrick <bradfitz@golang.org>2018-09-25 18:57:36 +0000
commit5bba5053675f102a3a81242e8f7551791ae5a56e (patch)
tree870d832100166714a667e490b22ff772662de9a6 /src/image
parente945623930d2c85b9a81476203451ca8f4092875 (diff)
downloadgo-5bba5053675f102a3a81242e8f7551791ae5a56e.tar.gz
go-5bba5053675f102a3a81242e8f7551791ae5a56e.zip
image/draw: optimize bounds checks in loops
Use subslices with known length and cap to give bounds checking hints to the compiler. Improves over the earlier pointer based optimizations in https://go-review.googlesource.com/c/go/+/14093 for GlyphOver but not for FillOver so the latter is left unchanged. See #27857 for discussion of small caps used in subslices. name old time/op new time/op delta FillOver-8 607µs ± 1% 609µs ± 1% ~ (p=0.447 n=9+10) FillSrc-8 23.0µs ± 1% 22.9µs ± 2% ~ (p=0.412 n=9+10) CopyOver-8 647µs ± 0% 560µs ± 0% -13.43% (p=0.000 n=9+10) CopySrc-8 19.3µs ± 1% 19.1µs ± 2% -0.66% (p=0.029 n=10+10) NRGBAOver-8 697µs ± 1% 651µs ± 1% -6.64% (p=0.000 n=10+10) NRGBASrc-8 405µs ± 1% 347µs ± 0% -14.23% (p=0.000 n=10+10) YCbCr-8 432µs ± 2% 431µs ± 1% ~ (p=0.764 n=10+9) Gray-8 164µs ± 1% 139µs ± 1% -15.44% (p=0.000 n=10+10) CMYK-8 498µs ± 0% 461µs ± 0% -7.49% (p=0.000 n=10+9) GlyphOver-8 220µs ± 0% 199µs ± 0% -9.52% (p=0.000 n=9+10) RGBA-8 3.81ms ± 5% 3.79ms ± 5% ~ (p=0.549 n=9+10) Paletted-8 1.73ms ± 0% 1.73ms ± 1% ~ (p=0.278 n=10+9) GenericOver-8 11.0ms ± 2% 11.0ms ± 1% ~ (p=0.842 n=9+10) GenericMaskOver-8 5.29ms ± 1% 5.30ms ± 0% ~ (p=0.182 n=9+10) GenericSrc-8 4.24ms ± 1% 4.24ms ± 0% ~ (p=0.436 n=9+9) GenericMaskSrc-8 7.89ms ± 1% 7.90ms ± 2% ~ (p=0.631 n=10+10) Change-Id: I6fe1b21bb5e255826cbfdd2e73efd5858cd5557c Reviewed-on: https://go-review.googlesource.com/136935 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/image')
-rw-r--r--src/image/draw/draw.go126
1 files changed, 63 insertions, 63 deletions
diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go
index 977d7c5221..3ff1828dc0 100644
--- a/src/image/draw/draw.go
+++ b/src/image/draw/draw.go
@@ -309,23 +309,20 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.
dpix := dst.Pix[d0:]
spix := src.Pix[s0:]
for i := i0; i != i1; i += idelta {
- sr := uint32(spix[i+0]) * 0x101
- sg := uint32(spix[i+1]) * 0x101
- sb := uint32(spix[i+2]) * 0x101
- sa := uint32(spix[i+3]) * 0x101
-
- dr := &dpix[i+0]
- dg := &dpix[i+1]
- db := &dpix[i+2]
- da := &dpix[i+3]
+ s := spix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+ sr := uint32(s[0]) * 0x101
+ sg := uint32(s[1]) * 0x101
+ sb := uint32(s[2]) * 0x101
+ sa := uint32(s[3]) * 0x101
// The 0x101 is here for the same reason as in drawRGBA.
a := (m - sa) * 0x101
- *dr = uint8((uint32(*dr)*a/m + sr) >> 8)
- *dg = uint8((uint32(*dg)*a/m + sg) >> 8)
- *db = uint8((uint32(*db)*a/m + sb) >> 8)
- *da = uint8((uint32(*da)*a/m + sa) >> 8)
+ d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+ d[0] = uint8((uint32(d[0])*a/m + sr) >> 8)
+ d[1] = uint8((uint32(d[1])*a/m + sg) >> 8)
+ d[2] = uint8((uint32(d[2])*a/m + sb) >> 8)
+ d[3] = uint8((uint32(d[3])*a/m + sa) >> 8)
}
d0 += ddelta
s0 += sdelta
@@ -372,23 +369,25 @@ func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp imag
for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
// Convert from non-premultiplied color to pre-multiplied color.
- sa := uint32(spix[si+3]) * 0x101
- sr := uint32(spix[si+0]) * sa / 0xff
- sg := uint32(spix[si+1]) * sa / 0xff
- sb := uint32(spix[si+2]) * sa / 0xff
-
- dr := uint32(dpix[i+0])
- dg := uint32(dpix[i+1])
- db := uint32(dpix[i+2])
- da := uint32(dpix[i+3])
+ s := spix[si : si+4 : si+4] // Small cap improves performance, see https://golang.org/issue/27857
+ sa := uint32(s[3]) * 0x101
+ sr := uint32(s[0]) * sa / 0xff
+ sg := uint32(s[1]) * sa / 0xff
+ sb := uint32(s[2]) * sa / 0xff
+
+ d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+ dr := uint32(d[0])
+ dg := uint32(d[1])
+ db := uint32(d[2])
+ da := uint32(d[3])
// The 0x101 is here for the same reason as in drawRGBA.
a := (m - sa) * 0x101
- dpix[i+0] = uint8((dr*a/m + sr) >> 8)
- dpix[i+1] = uint8((dg*a/m + sg) >> 8)
- dpix[i+2] = uint8((db*a/m + sb) >> 8)
- dpix[i+3] = uint8((da*a/m + sa) >> 8)
+ d[0] = uint8((dr*a/m + sr) >> 8)
+ d[1] = uint8((dg*a/m + sg) >> 8)
+ d[2] = uint8((db*a/m + sb) >> 8)
+ d[3] = uint8((da*a/m + sa) >> 8)
}
}
}
@@ -407,15 +406,17 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image
for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
// Convert from non-premultiplied color to pre-multiplied color.
- sa := uint32(spix[si+3]) * 0x101
- sr := uint32(spix[si+0]) * sa / 0xff
- sg := uint32(spix[si+1]) * sa / 0xff
- sb := uint32(spix[si+2]) * sa / 0xff
-
- dpix[i+0] = uint8(sr >> 8)
- dpix[i+1] = uint8(sg >> 8)
- dpix[i+2] = uint8(sb >> 8)
- dpix[i+3] = uint8(sa >> 8)
+ s := spix[si : si+4 : si+4] // Small cap improves performance, see https://golang.org/issue/27857
+ sa := uint32(s[3]) * 0x101
+ sr := uint32(s[0]) * sa / 0xff
+ sg := uint32(s[1]) * sa / 0xff
+ sb := uint32(s[2]) * sa / 0xff
+
+ d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+ d[0] = uint8(sr >> 8)
+ d[1] = uint8(sg >> 8)
+ d[2] = uint8(sb >> 8)
+ d[3] = uint8(sa >> 8)
}
}
}
@@ -434,10 +435,11 @@ func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Poin
for i, si := i0, si0; i < i1; i, si = i+4, si+1 {
p := spix[si]
- dpix[i+0] = p
- dpix[i+1] = p
- dpix[i+2] = p
- dpix[i+3] = 255
+ d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+ d[0] = p
+ d[1] = p
+ d[2] = p
+ d[3] = 255
}
}
}
@@ -455,9 +457,10 @@ func drawCMYK(dst *image.RGBA, r image.Rectangle, src *image.CMYK, sp image.Poin
spix := src.Pix[sy*src.Stride:]
for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
- dpix[i+0], dpix[i+1], dpix[i+2] =
- color.CMYKToRGB(spix[si+0], spix[si+1], spix[si+2], spix[si+3])
- dpix[i+3] = 255
+ s := spix[si : si+4 : si+4] // Small cap improves performance, see https://golang.org/issue/27857
+ d := dpix[i : i+4 : i+4]
+ d[0], d[1], d[2] = color.CMYKToRGB(s[0], s[1], s[2], s[3])
+ d[3] = 255
}
}
}
@@ -475,18 +478,14 @@ func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask
}
ma |= ma << 8
- dr := &dst.Pix[i+0]
- dg := &dst.Pix[i+1]
- db := &dst.Pix[i+2]
- da := &dst.Pix[i+3]
-
// The 0x101 is here for the same reason as in drawRGBA.
a := (m - (sa * ma / m)) * 0x101
- *dr = uint8((uint32(*dr)*a + sr*ma) / m >> 8)
- *dg = uint8((uint32(*dg)*a + sg*ma) / m >> 8)
- *db = uint8((uint32(*db)*a + sb*ma) / m >> 8)
- *da = uint8((uint32(*da)*a + sa*ma) / m >> 8)
+ d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+ d[0] = uint8((uint32(d[0])*a + sr*ma) / m >> 8)
+ d[1] = uint8((uint32(d[1])*a + sg*ma) / m >> 8)
+ d[2] = uint8((uint32(d[2])*a + sb*ma) / m >> 8)
+ d[3] = uint8((uint32(d[3])*a + sa*ma) / m >> 8)
}
i0 += dst.Stride
i1 += dst.Stride
@@ -518,11 +517,12 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
_, _, _, ma = mask.At(mx, my).RGBA()
}
sr, sg, sb, sa := src.At(sx, sy).RGBA()
+ d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
if op == Over {
- dr := uint32(dst.Pix[i+0])
- dg := uint32(dst.Pix[i+1])
- db := uint32(dst.Pix[i+2])
- da := uint32(dst.Pix[i+3])
+ dr := uint32(d[0])
+ dg := uint32(d[1])
+ db := uint32(d[2])
+ da := uint32(d[3])
// dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255].
// We work in 16-bit color, and so would normally do:
@@ -532,16 +532,16 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
// This yields the same result, but is fewer arithmetic operations.
a := (m - (sa * ma / m)) * 0x101
- dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8)
- dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8)
- dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8)
- dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8)
+ d[0] = uint8((dr*a + sr*ma) / m >> 8)
+ d[1] = uint8((dg*a + sg*ma) / m >> 8)
+ d[2] = uint8((db*a + sb*ma) / m >> 8)
+ d[3] = uint8((da*a + sa*ma) / m >> 8)
} else {
- dst.Pix[i+0] = uint8(sr * ma / m >> 8)
- dst.Pix[i+1] = uint8(sg * ma / m >> 8)
- dst.Pix[i+2] = uint8(sb * ma / m >> 8)
- dst.Pix[i+3] = uint8(sa * ma / m >> 8)
+ d[0] = uint8(sr * ma / m >> 8)
+ d[1] = uint8(sg * ma / m >> 8)
+ d[2] = uint8(sb * ma / m >> 8)
+ d[3] = uint8(sa * ma / m >> 8)
}
}
i0 += dy * dst.Stride