From faf179caa0d06ce119b5c26c117544fec70b38e8 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Tue, 16 Apr 2019 16:15:23 +0200 Subject: [PATCH] implemented clipping and drawImage --- backend/software/fill.go | 33 ++++++++++++++ backend/software/images.go | 29 +++++++++++-- backend/software/software.go | 19 ++++---- backend/software/triangles.go | 81 +++++++++++++++++++++++++++++++---- 4 files changed, 138 insertions(+), 24 deletions(-) diff --git a/backend/software/fill.go b/backend/software/fill.go index 235937e..89a46a1 100644 --- a/backend/software/fill.go +++ b/backend/software/fill.go @@ -9,6 +9,9 @@ import ( func (b *SoftwareBackend) Clear(pts [4][2]float64) { iterateTriangles(pts[:], func(tri [][2]float64) { b.fillTriangle(tri, func(x, y int) { + if b.clip.AlphaAt(x, y).A == 0 { + return + } 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) { iterateTriangles(pts[:], func(tri [][2]float64) { b.fillTriangle(tri, func(x, y int) { + if b.clip.AlphaAt(x, y).A == 0 { + return + } 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 + } + } +} diff --git a/backend/software/images.go b/backend/software/images.go index 0439326..287949a 100644 --- a/backend/software/images.go +++ b/backend/software/images.go @@ -7,7 +7,8 @@ import ( ) type Image struct { - img image.Image + img image.Image + deleted bool } 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) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [][2]float64) { + simg := dimg.(*Image) + if simg.deleted { + 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 { @@ -34,9 +41,23 @@ func (img *Image) Size() (w, h int) { } func (img *Image) Delete() { + img.deleted = true } func (img *Image) Replace(src image.Image) error { img.img = src 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 } diff --git a/backend/software/software.go b/backend/software/software.go index 2171ead..5945dbc 100644 --- a/backend/software/software.go +++ b/backend/software/software.go @@ -9,32 +9,29 @@ import ( type SoftwareBackend struct { Image *image.RGBA + clip *image.Alpha + clip2 *image.Alpha w, h int } func New(w, h int) *SoftwareBackend { - return &SoftwareBackend{ - Image: image.NewRGBA(image.Rect(0, 0, w, h)), - w: w, - h: h, - } + b := &SoftwareBackend{} + b.SetSize(w, h) + return b } func (b *SoftwareBackend) SetSize(w, h int) { b.w, b.h = 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) { 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 { return b.Image.SubImage(image.Rect(x, y, w, h)).(*image.RGBA) } diff --git a/backend/software/triangles.go b/backend/software/triangles.go index d3ddfc8..efa5edd 100644 --- a/backend/software/triangles.go +++ b/backend/software/triangles.go @@ -1,18 +1,12 @@ package softwarebackend -import "math" +import ( + "math" +) func triangleLR(tri [][2]float64, y float64) (l, r float64) { 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 if a[1] > b[1] { 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 if y >= a[1] && y <= b[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)) { if len(pts) == 4 { var buf [3][2]float64