Commit af08cfa4 authored by Nigel Tao's avatar Nigel Tao

image: add PixOffset methods; use them in image/draw and image/tiff.

image/draw benchmarks show <1% change for the fast paths.
The slow paths got worse by 1-4%, but they're the slow paths.
I don't care so much about them, and presumably compiler improvements
could claw it back.

IIUC 6g's inlining is enabled by default now.

benchmark                        old ns/op    new ns/op    delta
draw.BenchmarkFillOver             2988384      2999624   +0.38%
draw.BenchmarkFillSrc               153141       153262   +0.08%
draw.BenchmarkCopyOver             2155756      2170831   +0.70%
draw.BenchmarkCopySrc                72591        72646   +0.08%
draw.BenchmarkNRGBAOver            2487372      2491576   +0.17%
draw.BenchmarkNRGBASrc             1361306      1409180   +3.52%
draw.BenchmarkYCbCr                2540712      2562359   +0.85%
draw.BenchmarkGlyphOver            1004879      1023308   +1.83%
draw.BenchmarkRGBA                 8746670      8844455   +1.12%
draw.BenchmarkGenericOver         31860960     32512960   +2.05%
draw.BenchmarkGenericMaskOver     16369060     16435720   +0.41%
draw.BenchmarkGenericSrc          13128540     13127810   -0.01%
draw.BenchmarkGenericMaskSrc      30059300     28883210   -3.91%

R=r, gri
CC=golang-dev, rsc
https://golang.org/cl/5536059
parent 98af3880
...@@ -171,7 +171,7 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { ...@@ -171,7 +171,7 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
sr, sg, sb, sa := src.RGBA() sr, sg, sb, sa := src.RGBA()
// The 0x101 is here for the same reason as in drawRGBA. // The 0x101 is here for the same reason as in drawRGBA.
a := (m - sa) * 0x101 a := (m - sa) * 0x101
i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 i0 := dst.PixOffset(r.Min.X, r.Min.Y)
i1 := i0 + r.Dx()*4 i1 := i0 + r.Dx()*4
for y := r.Min.Y; y != r.Max.Y; y++ { for y := r.Min.Y; y != r.Max.Y; y++ {
for i := i0; i < i1; i += 4 { for i := i0; i < i1; i += 4 {
...@@ -195,7 +195,7 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { ...@@ -195,7 +195,7 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
// The built-in copy function is faster than a straightforward for loop to fill the destination with // The built-in copy function is faster than a straightforward for loop to fill the destination with
// the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and
// then use the first row as the slice source for the remaining rows. // then use the first row as the slice source for the remaining rows.
i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 i0 := dst.PixOffset(r.Min.X, r.Min.Y)
i1 := i0 + r.Dx()*4 i1 := i0 + r.Dx()*4
for i := i0; i < i1; i += 4 { for i := i0; i < i1; i += 4 {
dst.Pix[i+0] = uint8(sr >> 8) dst.Pix[i+0] = uint8(sr >> 8)
...@@ -213,8 +213,8 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { ...@@ -213,8 +213,8 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
dx, dy := r.Dx(), r.Dy() dx, dy := r.Dx(), r.Dy()
d0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 d0 := dst.PixOffset(r.Min.X, r.Min.Y)
s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + (sp.X-src.Rect.Min.X)*4 s0 := src.PixOffset(sp.X, sp.Y)
var ( var (
ddelta, sdelta int ddelta, sdelta int
i0, i1, idelta int i0, i1, idelta int
...@@ -261,8 +261,8 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image. ...@@ -261,8 +261,8 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.
func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
n, dy := 4*r.Dx(), r.Dy() n, dy := 4*r.Dx(), r.Dy()
d0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 d0 := dst.PixOffset(r.Min.X, r.Min.Y)
s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + (sp.X-src.Rect.Min.X)*4 s0 := src.PixOffset(sp.X, sp.Y)
var ddelta, sdelta int var ddelta, sdelta int
if r.Min.Y <= sp.Y { if r.Min.Y <= sp.Y {
ddelta = dst.Stride ddelta = dst.Stride
...@@ -348,9 +348,7 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image ...@@ -348,9 +348,7 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image
func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) { func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) {
// An image.YCbCr is always fully opaque, and so if the mask is implicitly nil // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src. // (i.e. fully opaque) then the op is effectively always Src.
var ( var yy, cb, cr uint8
yy, cb, cr uint8
)
x0 := (r.Min.X - dst.Rect.Min.X) * 4 x0 := (r.Min.X - dst.Rect.Min.X) * 4
x1 := (r.Max.X - dst.Rect.Min.X) * 4 x1 := (r.Max.X - dst.Rect.Min.X) * 4
y0 := r.Min.Y - dst.Rect.Min.Y y0 := r.Min.Y - dst.Rect.Min.Y
...@@ -405,9 +403,9 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po ...@@ -405,9 +403,9 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
} }
func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) {
i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 i0 := dst.PixOffset(r.Min.X, r.Min.Y)
i1 := i0 + r.Dx()*4 i1 := i0 + r.Dx()*4
mi0 := (mp.Y-mask.Rect.Min.Y)*mask.Stride + mp.X - mask.Rect.Min.X mi0 := mask.PixOffset(mp.X, mp.Y)
sr, sg, sb, sa := src.RGBA() sr, sg, sb, sa := src.RGBA()
for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 { for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 {
for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 { for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 {
...@@ -451,7 +449,7 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin ...@@ -451,7 +449,7 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
sx0 := sp.X + x0 - r.Min.X sx0 := sp.X + x0 - r.Min.X
mx0 := mp.X + x0 - r.Min.X mx0 := mp.X + x0 - r.Min.X
sx1 := sx0 + (x1 - x0) sx1 := sx0 + (x1 - x0)
i0 := (y0-dst.Rect.Min.Y)*dst.Stride + (x0-dst.Rect.Min.X)*4 i0 := dst.PixOffset(x0, y0)
di := dx * 4 di := dx * 4
for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx {
......
This diff is collapsed.
...@@ -223,8 +223,8 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) error { ...@@ -223,8 +223,8 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) error {
} }
case mRGB: case mRGB:
img := dst.(*image.RGBA) img := dst.(*image.RGBA)
min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 min := img.PixOffset(0, ymin)
max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 max := img.PixOffset(0, ymax)
var off int var off int
for i := min; i < max; i += 4 { for i := min; i < max; i += 4 {
img.Pix[i+0] = d.buf[off+0] img.Pix[i+0] = d.buf[off+0]
...@@ -235,16 +235,16 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) error { ...@@ -235,16 +235,16 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) error {
} }
case mNRGBA: case mNRGBA:
img := dst.(*image.NRGBA) img := dst.(*image.NRGBA)
min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 min := img.PixOffset(0, ymin)
max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 max := img.PixOffset(0, ymax)
if len(d.buf) != max-min { if len(d.buf) != max-min {
return FormatError("short data strip") return FormatError("short data strip")
} }
copy(img.Pix[min:max], d.buf) copy(img.Pix[min:max], d.buf)
case mRGBA: case mRGBA:
img := dst.(*image.RGBA) img := dst.(*image.RGBA)
min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 min := img.PixOffset(0, ymin)
max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 max := img.PixOffset(0, ymax)
if len(d.buf) != max-min { if len(d.buf) != max-min {
return FormatError("short data strip") return FormatError("short data strip")
} }
......
...@@ -49,28 +49,32 @@ func (p *YCbCr) At(x, y int) color.Color { ...@@ -49,28 +49,32 @@ func (p *YCbCr) At(x, y int) color.Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return color.YCbCr{} return color.YCbCr{}
} }
yi := p.YOffset(x, y)
ci := p.COffset(x, y)
return color.YCbCr{
p.Y[yi],
p.Cb[ci],
p.Cr[ci],
}
}
// YOffset returns the index of the first element of Y that corresponds to
// the pixel at (x, y).
func (p *YCbCr) YOffset(x, y int) int {
return y*p.YStride + x
}
// COffset returns the index of the first element of Cb or Cr that corresponds
// to the pixel at (x, y).
func (p *YCbCr) COffset(x, y int) int {
switch p.SubsampleRatio { switch p.SubsampleRatio {
case YCbCrSubsampleRatio422: case YCbCrSubsampleRatio422:
i := x / 2 return y*p.CStride + (x / 2)
return color.YCbCr{
p.Y[y*p.YStride+x],
p.Cb[y*p.CStride+i],
p.Cr[y*p.CStride+i],
}
case YCbCrSubsampleRatio420: case YCbCrSubsampleRatio420:
i, j := x/2, y/2 return (y/2)*p.CStride + (x / 2)
return color.YCbCr{
p.Y[y*p.YStride+x],
p.Cb[j*p.CStride+i],
p.Cr[j*p.CStride+i],
}
} }
// Default to 4:4:4 subsampling. // Default to 4:4:4 subsampling.
return color.YCbCr{ return y*p.CStride + x
p.Y[y*p.YStride+x],
p.Cb[y*p.CStride+x],
p.Cr[y*p.CStride+x],
}
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
......
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