implemented clipping and drawImage

This commit is contained in:
Thomas Friedel 2019-04-16 16:15:23 +02:00
parent 9f99dff89b
commit faf179caa0
4 changed files with 138 additions and 24 deletions

View file

@ -9,6 +9,9 @@ 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.fillTriangle(tri, func(x, y int) {
if b.clip.AlphaAt(x, y).A == 0 {
return
}
b.Image.SetRGBA(x, y, color.RGBA{}) b.Image.SetRGBA(x, y, color.RGBA{})
}) })
}) })
@ -17,7 +20,37 @@ 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) {
iterateTriangles(pts[:], func(tri [][2]float64) { iterateTriangles(pts[:], func(tri [][2]float64) {
b.fillTriangle(tri, func(x, y int) { b.fillTriangle(tri, func(x, y int) {
if b.clip.AlphaAt(x, y).A == 0 {
return
}
b.Image.SetRGBA(x, y, style.Color) b.Image.SetRGBA(x, y, style.Color)
}) })
}) })
} }
func (b *SoftwareBackend) ClearClip() {
p := b.clip.Pix
for i := range p {
p[i] = 255
}
}
func (b *SoftwareBackend) Clip(pts [][2]float64) {
p2 := b.clip2.Pix
for i := range p2 {
p2[i] = 0
}
iterateTriangles(pts[:], func(tri [][2]float64) {
b.fillTriangle(tri, func(x, y int) {
b.clip2.SetAlpha(x, y, color.Alpha{A: 255})
})
})
p := b.clip.Pix
for i := range p {
if p2[i] == 0 {
p[i] = 0
}
}
}

View file

@ -8,6 +8,7 @@ import (
type Image struct { type Image struct {
img image.Image img image.Image
deleted bool
} }
func (b *SoftwareBackend) LoadImage(img image.Image) (backendbase.Image, error) { func (b *SoftwareBackend) LoadImage(img image.Image) (backendbase.Image, error) {
@ -15,9 +16,15 @@ func (b *SoftwareBackend) LoadImage(img image.Image) (backendbase.Image, error)
} }
func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4][2]float64, alpha float64) { func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4][2]float64, alpha float64) {
} simg := dimg.(*Image)
if simg.deleted {
func (b *SoftwareBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [][2]float64) { return
}
b.fillQuad(pts, func(x, y int, sx2, sy2 float64) {
sxi := int(sx + sw*sx2 + 0.5)
syi := int(sy + sh*sy2 + 0.5)
b.Image.Set(x, y, simg.img.At(sxi, syi))
})
} }
func (img *Image) Width() int { func (img *Image) Width() int {
@ -34,9 +41,23 @@ func (img *Image) Size() (w, h int) {
} }
func (img *Image) Delete() { func (img *Image) Delete() {
img.deleted = true
} }
func (img *Image) Replace(src image.Image) error { func (img *Image) Replace(src image.Image) error {
img.img = src img.img = src
return nil return nil
} }
type ImagePattern struct {
data backendbase.ImagePatternData
}
func (b *SoftwareBackend) LoadImagePattern(data backendbase.ImagePatternData) backendbase.ImagePattern {
return &ImagePattern{
data: data,
}
}
func (ip *ImagePattern) Delete() {}
func (ip *ImagePattern) Replace(data backendbase.ImagePatternData) { ip.data = data }

View file

@ -9,32 +9,29 @@ import (
type SoftwareBackend struct { type SoftwareBackend struct {
Image *image.RGBA Image *image.RGBA
clip *image.Alpha
clip2 *image.Alpha
w, h int w, h int
} }
func New(w, h int) *SoftwareBackend { func New(w, h int) *SoftwareBackend {
return &SoftwareBackend{ b := &SoftwareBackend{}
Image: image.NewRGBA(image.Rect(0, 0, w, h)), b.SetSize(w, h)
w: w, return b
h: h,
}
} }
func (b *SoftwareBackend) SetSize(w, h int) { func (b *SoftwareBackend) SetSize(w, h int) {
b.w, b.h = w, h b.w, b.h = w, h
b.Image = image.NewRGBA(image.Rect(0, 0, w, h)) b.Image = image.NewRGBA(image.Rect(0, 0, w, h))
b.clip = image.NewAlpha(image.Rect(0, 0, w, h))
b.clip2 = image.NewAlpha(image.Rect(0, 0, w, h))
b.ClearClip()
} }
func (b *SoftwareBackend) Size() (int, int) { func (b *SoftwareBackend) Size() (int, int) {
return b.w, b.h return b.w, b.h
} }
func (b *SoftwareBackend) ClearClip() {
}
func (b *SoftwareBackend) Clip(pts [][2]float64) {
}
func (b *SoftwareBackend) GetImageData(x, y, w, h int) *image.RGBA { func (b *SoftwareBackend) GetImageData(x, y, w, h int) *image.RGBA {
return b.Image.SubImage(image.Rect(x, y, w, h)).(*image.RGBA) return b.Image.SubImage(image.Rect(x, y, w, h)).(*image.RGBA)
} }

View file

@ -1,18 +1,12 @@
package softwarebackend package softwarebackend
import "math" import (
"math"
)
func triangleLR(tri [][2]float64, y float64) (l, r float64) { func triangleLR(tri [][2]float64, y float64) (l, r float64) {
a, b, c := tri[0], tri[1], tri[2] a, b, c := tri[0], tri[1], tri[2]
// check general bounds
if y < a[1] && y < b[1] && y < c[1] {
return 0, 0
}
if y > a[1] && y > b[1] && y > c[1] {
return 0, 0
}
// sort by y // sort by y
if a[1] > b[1] { if a[1] > b[1] {
a, b = b, a a, b = b, a
@ -24,6 +18,14 @@ func triangleLR(tri [][2]float64, y float64) (l, r float64) {
} }
} }
// check general bounds
if y < a[1] {
return a[0], a[0]
}
if y > c[1] {
return c[0], c[0]
}
// find left and right x at y // find left and right x at y
if y >= a[1] && y <= b[1] { if y >= a[1] && y <= b[1] {
r0 := (y - a[1]) / (b[1] - a[1]) r0 := (y - a[1]) / (b[1] - a[1])
@ -76,6 +78,67 @@ func (b *SoftwareBackend) fillTriangle(tri [][2]float64, fn func(x, y int)) {
} }
} }
func (b *SoftwareBackend) fillQuad(quad [4][2]float64, fn func(x, y int, sx, sy float64)) {
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
}
if maxY < 0 {
return
} 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++ {
lf1, rf1 := triangleLR(tri1[:], float64(y)+0.5)
lf2, rf2 := triangleLR(tri2[:], float64(y)+0.5)
l := int(math.Floor(math.Min(lf1, lf2)))
r := int(math.Ceil(math.Max(rf1, rf2)))
if l < 0 {
l = 0
} else if l >= b.w {
continue
}
if r < 0 {
continue
} else if r >= b.w {
r = b.w - 1
}
v0 := [2]float64{float64(l) - quad[0][0], float64(y) - quad[0][1]}
sx0 := topv[0]*v0[0] + topv[1]*v0[1]
sy0 := leftv[0]*v0[0] + leftv[1]*v0[1]
v1 := [2]float64{float64(r) - quad[0][0], float64(y) - quad[0][1]}
sx1 := topv[0]*v1[0] + topv[1]*v1[1]
sy1 := leftv[0]*v1[0] + leftv[1]*v1[1]
sx, sy := sx0/topLen, sy0/leftLen
sxStep := (sx1 - sx0) / float64(r-l) / topLen
syStep := (sy1 - sy0) / float64(r-l) / leftLen
for x := l; x <= r; x++ {
fn(x, y, sx, sy)
sx += sxStep
sy += syStep
}
}
}
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