added code for MSAA, doesn't work properly yet though
This commit is contained in:
parent
d56f68b2e7
commit
554fa84a0a
4 changed files with 200 additions and 26 deletions
|
@ -5,6 +5,16 @@ import (
|
||||||
"math"
|
"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 {
|
func mix(src, dest color.Color) color.RGBA {
|
||||||
ir1, ig1, ib1, ia1 := src.RGBA()
|
ir1, ig1, ib1, ia1 := src.RGBA()
|
||||||
r1 := float64(ir1) / 65535.0
|
r1 := float64(ir1) / 65535.0
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
func (b *SoftwareBackend) Clear(pts [4][2]float64) {
|
func (b *SoftwareBackend) Clear(pts [4][2]float64) {
|
||||||
iterateTriangles(pts[:], func(tri [][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 {
|
if b.clip.AlphaAt(x, y).A == 0 {
|
||||||
return
|
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])
|
dirlen := math.Sqrt(dir[0]*dir[0] + dir[1]*dir[1])
|
||||||
dir[0] /= dirlen
|
dir[0] /= dirlen
|
||||||
dir[1] /= dirlen
|
dir[1] /= dirlen
|
||||||
b.fillTriangles(pts, func(x, y int) {
|
b.fillTriangles(pts, func(x, y float64) color.RGBA {
|
||||||
pos := [2]float64{float64(x) - from[0], float64(y) - from[1]}
|
pos := [2]float64{x - from[0], y - from[1]}
|
||||||
r := (pos[0]*dir[0] + pos[1]*dir[1]) / dirlen
|
r := (pos[0]*dir[0] + pos[1]*dir[1]) / dirlen
|
||||||
color := lg.data.ColorAt(r)
|
return lg.data.ColorAt(r)
|
||||||
b.Image.SetRGBA(x, y, mix(color, b.Image.RGBAAt(x, y)))
|
|
||||||
})
|
})
|
||||||
} else if rg := style.RadialGradient; rg != nil {
|
} else if rg := style.RadialGradient; rg != nil {
|
||||||
rg := rg.(*RadialGradient)
|
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}
|
to := [2]float64{style.Gradient.X1, style.Gradient.Y1}
|
||||||
radFrom := style.Gradient.RadFrom
|
radFrom := style.Gradient.RadFrom
|
||||||
radTo := style.Gradient.RadTo
|
radTo := style.Gradient.RadTo
|
||||||
b.fillTriangles(pts, func(x, y int) {
|
b.fillTriangles(pts, func(x, y float64) color.RGBA {
|
||||||
pos := [2]float64{float64(x), float64(y)}
|
pos := [2]float64{x, y}
|
||||||
oa := 0.5 * math.Sqrt(
|
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)-
|
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)*
|
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
|
o1 := (-oa + ob) / oc
|
||||||
o2 := (oa + ob) / oc
|
o2 := (oa + ob) / oc
|
||||||
if math.IsNaN(o1) && math.IsNaN(o2) {
|
if math.IsNaN(o1) && math.IsNaN(o2) {
|
||||||
return
|
return color.RGBA{}
|
||||||
}
|
}
|
||||||
o := math.Max(o1, o2)
|
o := math.Max(o1, o2)
|
||||||
col := rg.data.ColorAt(o)
|
return rg.data.ColorAt(o)
|
||||||
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
|
|
||||||
})
|
})
|
||||||
} else if ip := style.ImagePattern; ip != nil {
|
} else if ip := style.ImagePattern; ip != nil {
|
||||||
ip := ip.(*ImagePattern)
|
ip := ip.(*ImagePattern)
|
||||||
|
@ -66,16 +64,16 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
||||||
fw, fh := float64(w), float64(h)
|
fw, fh := float64(w), float64(h)
|
||||||
rx := ip.data.Repeat == backendbase.Repeat || ip.data.Repeat == backendbase.RepeatX
|
rx := ip.data.Repeat == backendbase.Repeat || ip.data.Repeat == backendbase.RepeatX
|
||||||
ry := ip.data.Repeat == backendbase.Repeat || ip.data.Repeat == backendbase.RepeatY
|
ry := ip.data.Repeat == backendbase.Repeat || ip.data.Repeat == backendbase.RepeatY
|
||||||
b.fillTriangles(pts, func(x, y int) {
|
b.fillTriangles(pts, func(x, y float64) color.RGBA {
|
||||||
pos := [2]float64{float64(x), float64(y)}
|
pos := [2]float64{x, y}
|
||||||
tfptx := pos[0]*ip.data.Transform[0] + pos[1]*ip.data.Transform[1] + ip.data.Transform[2]
|
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]
|
tfpty := pos[0]*ip.data.Transform[3] + pos[1]*ip.data.Transform[4] + ip.data.Transform[5]
|
||||||
|
|
||||||
if !rx && (tfptx < 0 || tfptx >= fw) {
|
if !rx && (tfptx < 0 || tfptx >= fw) {
|
||||||
return
|
return color.RGBA{}
|
||||||
}
|
}
|
||||||
if !ry && (tfpty < 0 || tfpty >= fh) {
|
if !ry && (tfpty < 0 || tfpty >= fh) {
|
||||||
return
|
return color.RGBA{}
|
||||||
}
|
}
|
||||||
|
|
||||||
mx := int(math.Floor(tfptx)) % w
|
mx := int(math.Floor(tfptx)) % w
|
||||||
|
@ -87,12 +85,11 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
||||||
my += h
|
my += h
|
||||||
}
|
}
|
||||||
|
|
||||||
col := mip.At(mx, my)
|
return toRGBA(mip.At(mx, my))
|
||||||
b.Image.SetRGBA(x, y, mix(col, b.Image.RGBAAt(x, y)))
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
b.fillTriangles(pts, func(x, y int) {
|
b.fillTriangles(pts, func(x, y float64) color.RGBA {
|
||||||
b.Image.SetRGBA(x, y, mix(style.Color, b.Image.RGBAAt(x, y)))
|
return style.Color
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +129,7 @@ func (b *SoftwareBackend) Clip(pts [][2]float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
iterateTriangles(pts[:], func(tri [][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})
|
b.mask.SetAlpha(x, y, color.Alpha{A: 255})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,9 +9,12 @@ import (
|
||||||
|
|
||||||
type SoftwareBackend struct {
|
type SoftwareBackend struct {
|
||||||
Image *image.RGBA
|
Image *image.RGBA
|
||||||
clip *image.Alpha
|
|
||||||
mask *image.Alpha
|
// MSAA int
|
||||||
w, h int
|
|
||||||
|
clip *image.Alpha
|
||||||
|
mask *image.Alpha
|
||||||
|
w, h int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(w, h int) *SoftwareBackend {
|
func New(w, h int) *SoftwareBackend {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func triangleLR(tri [][2]float64, y float64) (l, r float64, outside bool) {
|
||||||
return
|
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])))
|
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])))
|
maxY := int(math.Ceil(math.Max(math.Max(tri[0][1], tri[1][1]), tri[2][1])))
|
||||||
if minY < 0 {
|
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 {
|
func quadArea(quad [4][2]float64) float64 {
|
||||||
leftv := [2]float64{quad[1][0] - quad[0][0], quad[1][1] - quad[0][1]}
|
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]}
|
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) {
|
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 {
|
if b.clip.AlphaAt(x, y).A == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -191,7 +287,75 @@ func (b *SoftwareBackend) fillTriangles(pts [][2]float64, fn func(x, y int)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.mask.SetAlpha(x, y, color.Alpha{A: 255})
|
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)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue