implemented MSAA on quad drawing

This commit is contained in:
Thomas Friedel 2019-05-04 12:37:57 +02:00
parent 17b212acba
commit e0b88c0ca6
3 changed files with 219 additions and 19 deletions

View file

@ -20,8 +20,6 @@ func (b *SoftwareBackend) Clear(pts [4][2]float64) {
} }
func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
b.clearMask()
if lg := style.LinearGradient; lg != nil { if lg := style.LinearGradient; lg != nil {
lg := lg.(*LinearGradient) lg := lg.(*LinearGradient)
from := [2]float64{style.Gradient.X0, style.Gradient.Y0} from := [2]float64{style.Gradient.X0, style.Gradient.Y0}
@ -97,14 +95,14 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
func (b *SoftwareBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [4][2]float64) { func (b *SoftwareBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [4][2]float64) {
mw := float64(mask.Bounds().Dx()) mw := float64(mask.Bounds().Dx())
mh := float64(mask.Bounds().Dy()) mh := float64(mask.Bounds().Dy())
b.fillQuad(pts, func(x, y int, sx2, sy2 float64) { b.fillQuad(pts, func(x, y, sx2, sy2 float64) color.RGBA {
sxi := int(mw * sx2) sxi := int(mw * sx2)
syi := int(mh * sy2) syi := int(mh * sy2)
a := mask.AlphaAt(sxi, syi) a := mask.AlphaAt(sxi, syi)
if a.A == 0 { if a.A == 0 {
return return color.RGBA{}
} }
b.Image.SetRGBA(x, y, alphaColor(style.Color, a)) return alphaColor(style.Color, a)
}) })
} }

View file

@ -76,13 +76,12 @@ func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float
sw *= mipScaleX sw *= mipScaleX
sh *= mipScaleY sh *= mipScaleY
b.fillQuad(pts, func(x, y int, sx2, sy2 float64) { b.fillQuad(pts, func(x, y, tx, ty float64) color.RGBA {
imgx := sx + sw*sx2 imgx := sx + sw*tx
imgy := sy + sh*sy2 imgy := sy + sh*ty
imgxf := math.Floor(imgx) imgxf := math.Floor(imgx)
imgyf := math.Floor(imgy) imgyf := math.Floor(imgy)
c := mip.At(int(imgxf), int(imgyf)) return toRGBA(mip.At(int(imgxf), int(imgyf)))
b.Image.Set(x, y, c)
// rx := imgx - imgxf // rx := imgx - imgxf
// ry := imgy - imgyf // ry := imgy - imgyf

View file

@ -91,6 +91,7 @@ func (b *SoftwareBackend) fillTriangleNoAA(tri [][2]float64, fn func(x, y int))
type msaaPixel struct { type msaaPixel struct {
ix, iy int ix, iy int
fx, fy float64 fx, fy float64
tx, ty float64
} }
func (b *SoftwareBackend) fillTriangleMSAA(tri [][2]float64, msaaLevel int, msaaPixels []msaaPixel, fn func(x, y int)) []msaaPixel { func (b *SoftwareBackend) fillTriangleMSAA(tri [][2]float64, msaaLevel int, msaaPixels []msaaPixel, fn func(x, y int)) []msaaPixel {
@ -199,7 +200,7 @@ func quadArea(quad [4][2]float64) float64 {
return math.Abs(leftv[0]*topv[1] - leftv[1]*topv[0]) return math.Abs(leftv[0]*topv[1] - leftv[1]*topv[0])
} }
func (b *SoftwareBackend) fillQuad(quad [4][2]float64, fn func(x, y int, sx, sy float64)) { func (b *SoftwareBackend) fillQuadNoAA(quad [4][2]float64, fn func(x, y int, tx, ty float64)) {
minY := int(math.Floor(math.Min(math.Min(quad[0][1], quad[1][1]), math.Min(quad[2][1], quad[3][1])))) minY := int(math.Floor(math.Min(math.Min(quad[0][1], quad[1][1]), math.Min(quad[2][1], quad[3][1]))))
maxY := int(math.Ceil(math.Max(math.Max(quad[0][1], quad[1][1]), math.Max(quad[2][1], quad[3][1])))) maxY := int(math.Ceil(math.Max(math.Max(quad[0][1], quad[1][1]), math.Max(quad[2][1], quad[3][1]))))
if minY < 0 { if minY < 0 {
@ -246,29 +247,229 @@ func (b *SoftwareBackend) fillQuad(quad [4][2]float64, fn func(x, y int, sx, sy
continue continue
} }
sfy := float64(y) + 0.5 - quad[0][1] tfy := float64(y) + 0.5 - quad[0][1]
fl, cr := int(math.Floor(l)), int(math.Ceil(r)) fl, cr := int(math.Floor(l)), int(math.Ceil(r))
for x := fl; x <= cr; x++ { for x := fl; x <= cr; x++ {
fx := float64(x) + 0.5 fx := float64(x) + 0.5
if fx < l || fx >= r { if fx < l || fx >= r {
continue continue
} }
sfx := fx - quad[0][0] tfx := fx - quad[0][0]
var sx, sy float64 var tx, ty float64
if math.Abs(leftv[0]) > math.Abs(leftv[1]) { if math.Abs(leftv[0]) > math.Abs(leftv[1]) {
sx = (sfy - sfx*(leftv[1]/leftv[0])) / (topv[1] - topv[0]*(leftv[1]/leftv[0])) tx = (tfy - tfx*(leftv[1]/leftv[0])) / (topv[1] - topv[0]*(leftv[1]/leftv[0]))
sy = (sfx - topv[0]*sx) / leftv[0] ty = (tfx - topv[0]*tx) / leftv[0]
} else { } else {
sx = (sfx - sfy*(leftv[0]/leftv[1])) / (topv[0] - topv[1]*(leftv[0]/leftv[1])) tx = (tfx - tfy*(leftv[0]/leftv[1])) / (topv[0] - topv[1]*(leftv[0]/leftv[1]))
sy = (sfy - topv[1]*sx) / leftv[1] ty = (tfy - topv[1]*tx) / leftv[1]
} }
fn(x, y, sx/topLen, sy/leftLen) fn(x, y, tx/topLen, ty/leftLen)
} }
} }
} }
func (b *SoftwareBackend) fillQuadMSAA(quad [4][2]float64, msaaLevel int, msaaPixels []msaaPixel, fn func(x, y int, tx, ty float64)) []msaaPixel {
msaaStep := 1.0 / float64(msaaLevel+1)
minY := int(math.Floor(math.Min(math.Min(quad[0][1], quad[1][1]), math.Min(quad[2][1], quad[3][1]))))
maxY := int(math.Ceil(math.Max(math.Max(quad[0][1], quad[1][1]), math.Max(quad[2][1], quad[3][1]))))
if minY < 0 {
minY = 0
} else if minY >= b.h {
return msaaPixels
}
if maxY < 0 {
return msaaPixels
} else if maxY >= b.h {
maxY = b.h - 1
}
leftv := [2]float64{quad[1][0] - quad[0][0], quad[1][1] - quad[0][1]}
leftLen := math.Sqrt(leftv[0]*leftv[0] + leftv[1]*leftv[1])
leftv[0] /= leftLen
leftv[1] /= leftLen
topv := [2]float64{quad[3][0] - quad[0][0], quad[3][1] - quad[0][1]}
topLen := math.Sqrt(topv[0]*topv[0] + topv[1]*topv[1])
topv[0] /= topLen
topv[1] /= topLen
tri1 := [3][2]float64{quad[0], quad[1], quad[2]}
tri2 := [3][2]float64{quad[0], quad[2], quad[3]}
for y := minY; y <= maxY; y++ {
var l, r [5]float64
allOut := true
minL, maxR := math.MaxFloat64, 0.0
sy := float64(y) + msaaStep*0.5
for step := 0; step <= msaaLevel; step++ {
lf1, rf1, out1 := triangleLR(tri1[:], sy)
lf2, rf2, out2 := triangleLR(tri2[:], sy)
l[step] = math.Min(lf1, lf2)
r[step] = math.Max(rf1, rf2)
out := out1 || out2
if l[step] < 0 {
l[step] = 0
} else if l[step] > float64(b.w) {
l[step] = float64(b.w)
out = true
}
if r[step] < 0 {
r[step] = 0
out = true
} else if r[step] > float64(b.w) {
r[step] = float64(b.w)
}
if r[step] <= l[step] {
out = true
}
if !out {
allOut = false
minL = math.Min(minL, l[step])
maxR = math.Max(maxR, r[step])
}
sy += msaaStep
}
if allOut {
continue
}
fl, cr := int(math.Floor(minL)), int(math.Ceil(maxR))
for x := fl; x <= cr; x++ {
sy = float64(y) + msaaStep*0.5
allIn := true
check:
for stepy := 0; stepy <= msaaLevel; stepy++ {
sx := float64(x) + msaaStep*0.5
for stepx := 0; stepx <= msaaLevel; stepx++ {
if sx < l[stepy] || sx >= r[stepy] {
allIn = false
break check
}
sx += msaaStep
}
sy += msaaStep
}
if allIn {
tfx := float64(x) + 0.5 - quad[0][0]
tfy := float64(y) + 0.5 - quad[0][1]
var tx, ty float64
if math.Abs(leftv[0]) > math.Abs(leftv[1]) {
tx = (tfy - tfx*(leftv[1]/leftv[0])) / (topv[1] - topv[0]*(leftv[1]/leftv[0]))
ty = (tfx - topv[0]*tx) / leftv[0]
} else {
tx = (tfx - tfy*(leftv[0]/leftv[1])) / (topv[0] - topv[1]*(leftv[0]/leftv[1]))
ty = (tfy - topv[1]*tx) / leftv[1]
}
fn(x, y, tx/topLen, ty/leftLen)
continue
}
sy = float64(y) + msaaStep*0.5
for stepy := 0; stepy <= msaaLevel; stepy++ {
sx := float64(x) + msaaStep*0.5
for stepx := 0; stepx <= msaaLevel; stepx++ {
if sx >= l[stepy] && sx < r[stepy] {
tfx := sx - quad[0][0]
tfy := sy - quad[0][1]
var tx, ty float64
if math.Abs(leftv[0]) > math.Abs(leftv[1]) {
tx = (tfy - tfx*(leftv[1]/leftv[0])) / (topv[1] - topv[0]*(leftv[1]/leftv[0]))
ty = (tfx - topv[0]*tx) / leftv[0]
} else {
tx = (tfx - tfy*(leftv[0]/leftv[1])) / (topv[0] - topv[1]*(leftv[0]/leftv[1]))
ty = (tfy - topv[1]*tx) / leftv[1]
}
msaaPixels = addMSAAPixel(msaaPixels, msaaPixel{ix: x, iy: y, fx: sx, fy: sy, tx: tx / topLen, ty: ty / leftLen})
}
sx += msaaStep
}
sy += msaaStep
}
}
}
return msaaPixels
}
func (b *SoftwareBackend) fillQuad(pts [4][2]float64, fn func(x, y, tx, ty float64) color.RGBA) {
b.clearMask()
if b.MSAA > 0 {
var msaaPixelBuf [500]msaaPixel
msaaPixels := msaaPixelBuf[:0]
msaaPixels = b.fillQuadMSAA(pts, b.MSAA, msaaPixels, func(x, y int, tx, ty float64) {
if b.clip.AlphaAt(x, y).A == 0 {
return
}
if b.mask.AlphaAt(x, y).A > 0 {
return
}
b.mask.SetAlpha(x, y, color.Alpha{A: 255})
col := fn(float64(x)+0.5, float64(y)+0.5, tx, ty)
if col.A > 0 {
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
}
})
samples := (b.MSAA + 1) * (b.MSAA + 1)
for i, px := range msaaPixels {
if px.ix < 0 || b.clip.AlphaAt(px.ix, px.iy).A == 0 || b.mask.AlphaAt(px.ix, px.iy).A > 0 {
continue
}
b.mask.SetAlpha(px.ix, px.iy, color.Alpha{A: 255})
var mr, mg, mb, ma int
for j, px2 := range msaaPixels[i:] {
if px2.ix != px.ix || px2.iy != px.iy {
continue
}
col := fn(px2.fx, px2.fy, px2.tx, px2.ty)
mr += int(col.R)
mg += int(col.G)
mb += int(col.B)
ma += int(col.A)
msaaPixels[i+j].ix = -1
}
combined := color.RGBA{
R: uint8(mr / samples),
G: uint8(mg / samples),
B: uint8(mb / samples),
A: uint8(ma / samples),
}
b.Image.SetRGBA(px.ix, px.iy, mix(combined, b.Image.RGBAAt(px.ix, px.iy)))
}
} else {
b.fillQuadNoAA(pts, func(x, y int, tx, ty float64) {
if b.clip.AlphaAt(x, y).A == 0 {
return
}
if b.mask.AlphaAt(x, y).A > 0 {
return
}
b.mask.SetAlpha(x, y, color.Alpha{A: 255})
col := fn(float64(x)+0.5, float64(y)+0.5, tx, ty)
if col.A > 0 {
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
}
})
}
}
func iterateTriangles(pts [][2]float64, fn func(tri [][2]float64)) { func iterateTriangles(pts [][2]float64, fn func(tri [][2]float64)) {
if len(pts) == 4 { if len(pts) == 4 {
var buf [3][2]float64 var buf [3][2]float64
@ -358,6 +559,8 @@ func (b *SoftwareBackend) fillTrianglesMSAA(pts [][2]float64, msaaLevel int, fn
} }
func (b *SoftwareBackend) fillTriangles(pts [][2]float64, fn func(x, y float64) color.RGBA) { func (b *SoftwareBackend) fillTriangles(pts [][2]float64, fn func(x, y float64) color.RGBA) {
b.clearMask()
if b.MSAA > 0 { if b.MSAA > 0 {
b.fillTrianglesMSAA(pts, b.MSAA, fn) b.fillTrianglesMSAA(pts, b.MSAA, fn)
} else { } else {