summaryrefslogtreecommitdiff
path: root/libgo/go/image/draw/draw.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/image/draw/draw.go')
-rw-r--r--libgo/go/image/draw/draw.go329
1 files changed, 328 insertions, 1 deletions
diff --git a/libgo/go/image/draw/draw.go b/libgo/go/image/draw/draw.go
index 13f66682938..7dd18dfdb59 100644
--- a/libgo/go/image/draw/draw.go
+++ b/libgo/go/image/draw/draw.go
@@ -119,7 +119,8 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
return
}
- // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
+ // Fast paths for special cases. If none of them apply, then we fall back
+ // to general but slower implementations.
switch dst0 := dst.(type) {
case *image.RGBA:
if op == Over {
@@ -159,6 +160,17 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
case *image.Uniform:
drawGlyphOver(dst0, r, src0, mask0, mp)
return
+ case *image.RGBA:
+ drawRGBAMaskOver(dst0, r, src0, sp, mask0, mp)
+ return
+ case *image.Gray:
+ drawGrayMaskOver(dst0, r, src0, sp, mask0, mp)
+ return
+ // Case order matters. The next case (image.RGBA64Image) is an
+ // interface type that the concrete types above also implement.
+ case image.RGBA64Image:
+ drawRGBA64ImageMaskOver(dst0, r, src0, sp, mask0, mp)
+ return
}
}
} else {
@@ -219,6 +231,88 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
y0, y1, dy = y1-1, y0-1, -1
}
+ // FALLBACK1.17
+ //
+ // Try the draw.RGBA64Image and image.RGBA64Image interfaces, part of the
+ // standard library since Go 1.17. These are like the draw.Image and
+ // image.Image interfaces but they can avoid allocations from converting
+ // concrete color types to the color.Color interface type.
+
+ if dst0, _ := dst.(RGBA64Image); dst0 != nil {
+ if src0, _ := src.(image.RGBA64Image); src0 != nil {
+ if mask == nil {
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
+ sx := sp.X + x0 - r.Min.X
+ mx := mp.X + x0 - r.Min.X
+ for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
+ if op == Src {
+ dst0.SetRGBA64(x, y, src0.RGBA64At(sx, sy))
+ } else {
+ srgba := src0.RGBA64At(sx, sy)
+ a := m - uint32(srgba.A)
+ drgba := dst0.RGBA64At(x, y)
+ dst0.SetRGBA64(x, y, color.RGBA64{
+ R: uint16((uint32(drgba.R)*a)/m) + srgba.R,
+ G: uint16((uint32(drgba.G)*a)/m) + srgba.G,
+ B: uint16((uint32(drgba.B)*a)/m) + srgba.B,
+ A: uint16((uint32(drgba.A)*a)/m) + srgba.A,
+ })
+ }
+ }
+ }
+ return
+
+ } else if mask0, _ := mask.(image.RGBA64Image); mask0 != nil {
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
+ sx := sp.X + x0 - r.Min.X
+ mx := mp.X + x0 - r.Min.X
+ for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
+ ma := uint32(mask0.RGBA64At(mx, my).A)
+ switch {
+ case ma == 0:
+ if op == Over {
+ // No-op.
+ } else {
+ dst0.SetRGBA64(x, y, color.RGBA64{})
+ }
+ case ma == m && op == Src:
+ dst0.SetRGBA64(x, y, src0.RGBA64At(sx, sy))
+ default:
+ srgba := src0.RGBA64At(sx, sy)
+ if op == Over {
+ drgba := dst0.RGBA64At(x, y)
+ a := m - (uint32(srgba.A) * ma / m)
+ dst0.SetRGBA64(x, y, color.RGBA64{
+ R: uint16((uint32(drgba.R)*a + uint32(srgba.R)*ma) / m),
+ G: uint16((uint32(drgba.G)*a + uint32(srgba.G)*ma) / m),
+ B: uint16((uint32(drgba.B)*a + uint32(srgba.B)*ma) / m),
+ A: uint16((uint32(drgba.A)*a + uint32(srgba.A)*ma) / m),
+ })
+ } else {
+ dst0.SetRGBA64(x, y, color.RGBA64{
+ R: uint16(uint32(srgba.R) * ma / m),
+ G: uint16(uint32(srgba.G) * ma / m),
+ B: uint16(uint32(srgba.B) * ma / m),
+ A: uint16(uint32(srgba.A) * ma / m),
+ })
+ }
+ }
+ }
+ }
+ return
+ }
+ }
+ }
+
+ // FALLBACK1.0
+ //
+ // If none of the faster code paths above apply, use the draw.Image and
+ // image.Image interfaces, part of the standard library since Go 1.0.
+
var out color.RGBA64
sy := sp.Y + y0 - r.Min.Y
my := mp.Y + y0 - r.Min.Y
@@ -519,6 +613,156 @@ func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask
}
}
+func drawGrayMaskOver(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point, mask *image.Alpha, mp image.Point) {
+ x0, x1, dx := r.Min.X, r.Max.X, 1
+ y0, y1, dy := r.Min.Y, r.Max.Y, 1
+ if r.Overlaps(r.Add(sp.Sub(r.Min))) {
+ if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
+ }
+ }
+
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ sx0 := sp.X + x0 - r.Min.X
+ mx0 := mp.X + x0 - r.Min.X
+ sx1 := sx0 + (x1 - x0)
+ i0 := dst.PixOffset(x0, y0)
+ di := dx * 4
+ 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 {
+ mi := mask.PixOffset(mx, my)
+ ma := uint32(mask.Pix[mi])
+ ma |= ma << 8
+ si := src.PixOffset(sx, sy)
+ sy := uint32(src.Pix[si])
+ sy |= sy << 8
+ sa := uint32(0xffff)
+
+ d := dst.Pix[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])
+
+ // 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:
+ // dr |= dr << 8
+ // and similarly for dg, db and da, but instead we multiply a
+ // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
+ // This yields the same result, but is fewer arithmetic operations.
+ a := (m - (sa * ma / m)) * 0x101
+
+ d[0] = uint8((dr*a + sy*ma) / m >> 8)
+ d[1] = uint8((dg*a + sy*ma) / m >> 8)
+ d[2] = uint8((db*a + sy*ma) / m >> 8)
+ d[3] = uint8((da*a + sa*ma) / m >> 8)
+ }
+ i0 += dy * dst.Stride
+ }
+}
+
+func drawRGBAMaskOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point, mask *image.Alpha, mp image.Point) {
+ x0, x1, dx := r.Min.X, r.Max.X, 1
+ y0, y1, dy := r.Min.Y, r.Max.Y, 1
+ if dst == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
+ if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
+ }
+ }
+
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ sx0 := sp.X + x0 - r.Min.X
+ mx0 := mp.X + x0 - r.Min.X
+ sx1 := sx0 + (x1 - x0)
+ i0 := dst.PixOffset(x0, y0)
+ di := dx * 4
+ 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 {
+ mi := mask.PixOffset(mx, my)
+ ma := uint32(mask.Pix[mi])
+ ma |= ma << 8
+ si := src.PixOffset(sx, sy)
+ sr := uint32(src.Pix[si+0])
+ sg := uint32(src.Pix[si+1])
+ sb := uint32(src.Pix[si+2])
+ sa := uint32(src.Pix[si+3])
+ sr |= sr << 8
+ sg |= sg << 8
+ sb |= sb << 8
+ sa |= sa << 8
+ d := dst.Pix[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])
+
+ // 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:
+ // dr |= dr << 8
+ // and similarly for dg, db and da, but instead we multiply a
+ // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
+ // This yields the same result, but is fewer arithmetic operations.
+ a := (m - (sa * ma / m)) * 0x101
+
+ 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)
+ }
+ i0 += dy * dst.Stride
+ }
+}
+
+func drawRGBA64ImageMaskOver(dst *image.RGBA, r image.Rectangle, src image.RGBA64Image, sp image.Point, mask *image.Alpha, mp image.Point) {
+ x0, x1, dx := r.Min.X, r.Max.X, 1
+ y0, y1, dy := r.Min.Y, r.Max.Y, 1
+ if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
+ if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
+ }
+ }
+
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ sx0 := sp.X + x0 - r.Min.X
+ mx0 := mp.X + x0 - r.Min.X
+ sx1 := sx0 + (x1 - x0)
+ i0 := dst.PixOffset(x0, y0)
+ di := dx * 4
+ 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 {
+ mi := mask.PixOffset(mx, my)
+ ma := uint32(mask.Pix[mi])
+ ma |= ma << 8
+ srgba := src.RGBA64At(sx, sy)
+ d := dst.Pix[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])
+
+ // 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:
+ // dr |= dr << 8
+ // and similarly for dg, db and da, but instead we multiply a
+ // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
+ // This yields the same result, but is fewer arithmetic operations.
+ a := (m - (uint32(srgba.A) * ma / m)) * 0x101
+
+ d[0] = uint8((dr*a + uint32(srgba.R)*ma) / m >> 8)
+ d[1] = uint8((dg*a + uint32(srgba.G)*ma) / m >> 8)
+ d[2] = uint8((db*a + uint32(srgba.B)*ma) / m >> 8)
+ d[3] = uint8((da*a + uint32(srgba.A)*ma) / m >> 8)
+ }
+ i0 += dy * dst.Stride
+ }
+}
+
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
x0, x1, dx := r.Min.X, r.Max.X, 1
y0, y1, dy := r.Min.Y, r.Max.Y, 1
@@ -536,6 +780,89 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
sx1 := sx0 + (x1 - x0)
i0 := dst.PixOffset(x0, y0)
di := dx * 4
+
+ // Try the image.RGBA64Image interface, part of the standard library since
+ // Go 1.17.
+ //
+ // This optimization is similar to how FALLBACK1.17 optimizes FALLBACK1.0
+ // in DrawMask, except here the concrete type of dst is known to be
+ // *image.RGBA.
+ if src0, _ := src.(image.RGBA64Image); src0 != nil {
+ if mask == nil {
+ if op == Over {
+ 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 {
+ srgba := src0.RGBA64At(sx, sy)
+ d := dst.Pix[i : i+4 : i+4]
+ dr := uint32(d[0])
+ dg := uint32(d[1])
+ db := uint32(d[2])
+ da := uint32(d[3])
+ a := (m - uint32(srgba.A)) * 0x101
+ d[0] = uint8((dr*a/m + uint32(srgba.R)) >> 8)
+ d[1] = uint8((dg*a/m + uint32(srgba.G)) >> 8)
+ d[2] = uint8((db*a/m + uint32(srgba.B)) >> 8)
+ d[3] = uint8((da*a/m + uint32(srgba.A)) >> 8)
+ }
+ i0 += dy * dst.Stride
+ }
+ } else {
+ 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 {
+ srgba := src0.RGBA64At(sx, sy)
+ d := dst.Pix[i : i+4 : i+4]
+ d[0] = uint8(srgba.R >> 8)
+ d[1] = uint8(srgba.G >> 8)
+ d[2] = uint8(srgba.B >> 8)
+ d[3] = uint8(srgba.A >> 8)
+ }
+ i0 += dy * dst.Stride
+ }
+ }
+ return
+
+ } else if mask0, _ := mask.(image.RGBA64Image); mask0 != nil {
+ if op == Over {
+ 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 {
+ ma := uint32(mask0.RGBA64At(mx, my).A)
+ srgba := src0.RGBA64At(sx, sy)
+ d := dst.Pix[i : i+4 : i+4]
+ dr := uint32(d[0])
+ dg := uint32(d[1])
+ db := uint32(d[2])
+ da := uint32(d[3])
+ a := (m - (uint32(srgba.A) * ma / m)) * 0x101
+ d[0] = uint8((dr*a + uint32(srgba.R)*ma) / m >> 8)
+ d[1] = uint8((dg*a + uint32(srgba.G)*ma) / m >> 8)
+ d[2] = uint8((db*a + uint32(srgba.B)*ma) / m >> 8)
+ d[3] = uint8((da*a + uint32(srgba.A)*ma) / m >> 8)
+ }
+ i0 += dy * dst.Stride
+ }
+ } else {
+ 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 {
+ ma := uint32(mask0.RGBA64At(mx, my).A)
+ srgba := src0.RGBA64At(sx, sy)
+ d := dst.Pix[i : i+4 : i+4]
+ d[0] = uint8(uint32(srgba.R) * ma / m >> 8)
+ d[1] = uint8(uint32(srgba.G) * ma / m >> 8)
+ d[2] = uint8(uint32(srgba.B) * ma / m >> 8)
+ d[3] = uint8(uint32(srgba.A) * ma / m >> 8)
+ }
+ i0 += dy * dst.Stride
+ }
+ }
+ return
+ }
+ }
+
+ // Use the image.Image interface, part of the standard library since Go
+ // 1.0.
+ //
+ // This is similar to FALLBACK1.0 in DrawMask, except here the concrete
+ // type of dst is known to be *image.RGBA.
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 {
ma := uint32(m)