added code for MSAA, doesn't work properly yet though

This commit is contained in:
Thomas Friedel 2019-05-03 12:10:05 +02:00
parent d56f68b2e7
commit 554fa84a0a
4 changed files with 200 additions and 26 deletions

View file

@ -5,6 +5,16 @@ import (
"math"
)
func toRGBA(src color.Color) color.RGBA {
ir, ig, ib, ia := src.RGBA()
return color.RGBA{
R: uint8(ir >> 8),
G: uint8(ig >> 8),
B: uint8(ib >> 8),
A: uint8(ia >> 8),
}
}
func mix(src, dest color.Color) color.RGBA {
ir1, ig1, ib1, ia1 := src.RGBA()
r1 := float64(ir1) / 65535.0

View file

@ -10,7 +10,7 @@ import (
func (b *SoftwareBackend) Clear(pts [4][2]float64) {
iterateTriangles(pts[:], func(tri [][2]float64) {
b.fillTriangle(tri, func(x, y int) {
b.fillTriangleNoAA(tri, func(x, y int) {
if b.clip.AlphaAt(x, y).A == 0 {
return
}
@ -29,11 +29,10 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
dirlen := math.Sqrt(dir[0]*dir[0] + dir[1]*dir[1])
dir[0] /= dirlen
dir[1] /= dirlen
b.fillTriangles(pts, func(x, y int) {
pos := [2]float64{float64(x) - from[0], float64(y) - from[1]}
b.fillTriangles(pts, func(x, y float64) color.RGBA {
pos := [2]float64{x - from[0], y - from[1]}
r := (pos[0]*dir[0] + pos[1]*dir[1]) / dirlen
color := lg.data.ColorAt(r)
b.Image.SetRGBA(x, y, mix(color, b.Image.RGBAAt(x, y)))
return lg.data.ColorAt(r)
})
} else if rg := style.RadialGradient; rg != nil {
rg := rg.(*RadialGradient)
@ -41,8 +40,8 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
to := [2]float64{style.Gradient.X1, style.Gradient.Y1}
radFrom := style.Gradient.RadFrom
radTo := style.Gradient.RadTo
b.fillTriangles(pts, func(x, y int) {
pos := [2]float64{float64(x), float64(y)}
b.fillTriangles(pts, func(x, y float64) color.RGBA {
pos := [2]float64{x, y}
oa := 0.5 * math.Sqrt(
math.Pow(-2.0*from[0]*from[0]+2.0*from[0]*to[0]+2.0*from[0]*pos[0]-2.0*to[0]*pos[0]-2.0*from[1]*from[1]+2.0*from[1]*to[1]+2.0*from[1]*pos[1]-2.0*to[1]*pos[1]+2.0*radFrom*radFrom-2.0*radFrom*radTo, 2.0)-
4.0*(from[0]*from[0]-2.0*from[0]*pos[0]+pos[0]*pos[0]+from[1]*from[1]-2.0*from[1]*pos[1]+pos[1]*pos[1]-radFrom*radFrom)*
@ -52,11 +51,10 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
o1 := (-oa + ob) / oc
o2 := (oa + ob) / oc
if math.IsNaN(o1) && math.IsNaN(o2) {
return
return color.RGBA{}
}
o := math.Max(o1, o2)
col := rg.data.ColorAt(o)
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
return rg.data.ColorAt(o)
})
} else if ip := style.ImagePattern; ip != nil {
ip := ip.(*ImagePattern)
@ -66,16 +64,16 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
fw, fh := float64(w), float64(h)
rx := ip.data.Repeat == backendbase.Repeat || ip.data.Repeat == backendbase.RepeatX
ry := ip.data.Repeat == backendbase.Repeat || ip.data.Repeat == backendbase.RepeatY
b.fillTriangles(pts, func(x, y int) {
pos := [2]float64{float64(x), float64(y)}
b.fillTriangles(pts, func(x, y float64) color.RGBA {
pos := [2]float64{x, y}
tfptx := pos[0]*ip.data.Transform[0] + pos[1]*ip.data.Transform[1] + ip.data.Transform[2]
tfpty := pos[0]*ip.data.Transform[3] + pos[1]*ip.data.Transform[4] + ip.data.Transform[5]
if !rx && (tfptx < 0 || tfptx >= fw) {
return
return color.RGBA{}
}
if !ry && (tfpty < 0 || tfpty >= fh) {
return
return color.RGBA{}
}
mx := int(math.Floor(tfptx)) % w
@ -87,12 +85,11 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
my += h
}
col := mip.At(mx, my)
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
return toRGBA(mip.At(mx, my))
})
} else {
b.fillTriangles(pts, func(x, y int) {
b.Image.SetRGBA(x, y, mix(style.Color, b.Image.RGBAAt(x, y)))
b.fillTriangles(pts, func(x, y float64) color.RGBA {
return style.Color
})
}
}
@ -132,7 +129,7 @@ func (b *SoftwareBackend) Clip(pts [][2]float64) {
}
iterateTriangles(pts[:], func(tri [][2]float64) {
b.fillTriangle(tri, func(x, y int) {
b.fillTriangleNoAA(tri, func(x, y int) {
b.mask.SetAlpha(x, y, color.Alpha{A: 255})
})
})

View file

@ -9,6 +9,9 @@ import (
type SoftwareBackend struct {
Image *image.RGBA
// MSAA int
clip *image.Alpha
mask *image.Alpha
w, h int

View file

@ -46,7 +46,7 @@ func triangleLR(tri [][2]float64, y float64) (l, r float64, outside bool) {
return
}
func (b *SoftwareBackend) fillTriangle(tri [][2]float64, fn func(x, y int)) {
func (b *SoftwareBackend) fillTriangleNoAA(tri [][2]float64, fn func(x, y int)) {
minY := int(math.Floor(math.Min(math.Min(tri[0][1], tri[1][1]), tri[2][1])))
maxY := int(math.Ceil(math.Max(math.Max(tri[0][1], tri[1][1]), tri[2][1])))
if minY < 0 {
@ -88,6 +88,102 @@ func (b *SoftwareBackend) fillTriangle(tri [][2]float64, fn func(x, y int)) {
}
}
type msaaPixel struct {
ix, iy int
fx, fy float64
}
func (b *SoftwareBackend) fillTriangleMSAA(tri [][2]float64, msaaLevel int, msaaPixels []msaaPixel, fn func(x, y int)) []msaaPixel {
msaaStep := 1.0 / float64(msaaLevel+1)
minY := int(math.Floor(math.Min(math.Min(tri[0][1], tri[1][1]), tri[2][1])))
maxY := int(math.Ceil(math.Max(math.Max(tri[0][1], tri[1][1]), tri[2][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
}
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++ {
var out bool
l[step], r[step], out = triangleLR(tri, sy)
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 {
fn(x, y)
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] {
msaaPixels = append(msaaPixels, msaaPixel{ix: x, iy: y, fx: sx, fy: sy})
}
sx += msaaStep
}
sy += msaaStep
}
}
}
return msaaPixels
}
func quadArea(quad [4][2]float64) float64 {
leftv := [2]float64{quad[1][0] - quad[0][0], quad[1][1] - quad[0][1]}
topv := [2]float64{quad[3][0] - quad[0][0], quad[3][1] - quad[0][1]}
@ -181,9 +277,9 @@ func iterateTriangles(pts [][2]float64, fn func(tri [][2]float64)) {
}
}
func (b *SoftwareBackend) fillTriangles(pts [][2]float64, fn func(x, y int)) {
func (b *SoftwareBackend) fillTrianglesNoAA(pts [][2]float64, fn func(x, y float64) color.RGBA) {
iterateTriangles(pts[:], func(tri [][2]float64) {
b.fillTriangle(tri, func(x, y int) {
b.fillTriangleNoAA(tri, func(x, y int) {
if b.clip.AlphaAt(x, y).A == 0 {
return
}
@ -191,7 +287,75 @@ func (b *SoftwareBackend) fillTriangles(pts [][2]float64, fn func(x, y int)) {
return
}
b.mask.SetAlpha(x, y, color.Alpha{A: 255})
fn(x, y)
col := fn(float64(x), float64(y))
if col.A > 0 {
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
}
})
})
}
func (b *SoftwareBackend) fillTrianglesMSAA(pts [][2]float64, msaaLevel int, fn func(x, y float64) color.RGBA) {
var msaaPixelBuf [500]msaaPixel
msaaPixels := msaaPixelBuf[:0]
iterateTriangles(pts[:], func(tri [][2]float64) {
msaaPixels = b.fillTriangleMSAA(tri, msaaLevel, msaaPixels, func(x, y int) {
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), float64(y))
if col.A > 0 {
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
}
})
})
samples := (msaaLevel + 1) * (msaaLevel + 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(px.fx, px.fy)
if col.A == 0 {
return
}
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)))
}
}
func (b *SoftwareBackend) fillTriangles(pts [][2]float64, fn func(x, y float64) color.RGBA) {
// if b.MSAA > 0 {
// b.fillTrianglesMSAA(pts, b.MSAA, fn)
// } else {
b.fillTrianglesNoAA(pts, fn)
// }
}