diff --git a/backend/softwarebackend/images.go b/backend/softwarebackend/images.go index 6fea22d..dfadce9 100644 --- a/backend/softwarebackend/images.go +++ b/backend/softwarebackend/images.go @@ -2,18 +2,45 @@ package softwarebackend import ( "image" + "image/color" "math" "github.com/tfriedel6/canvas/backend/backendbase" ) type Image struct { - img image.Image + mips []image.Image deleted bool } func (b *SoftwareBackend) LoadImage(img image.Image) (backendbase.Image, error) { - return &Image{img: img}, nil + bimg := &Image{mips: make([]image.Image, 1, 10)} + bimg.Replace(img) + return bimg, nil +} + +func halveImage(img image.Image) (*image.RGBA, int, int) { + bounds := img.Bounds() + w, h := bounds.Dx(), bounds.Dy() + w = w / 2 + h = h / 2 + rimg := image.NewRGBA(image.Rect(0, 0, w, h)) + for y := 0; y < h; y++ { + sy := y * 2 + for x := 0; x < w; x++ { + sx := x * 2 + r1, g1, b1, a1 := img.At(sx, sy).RGBA() + r2, g2, b2, a2 := img.At(sx+1, sy).RGBA() + r3, g3, b3, a3 := img.At(sx, sy+1).RGBA() + r4, g4, b4, a4 := img.At(sx+1, sy+1).RGBA() + mixr := uint8((int(r1) + int(r2) + int(r3) + int(r4)) / 1024) + mixg := uint8((int(g1) + int(g2) + int(g3) + int(g4)) / 1024) + mixb := uint8((int(b1) + int(b2) + int(b3) + int(b4)) / 1024) + mixa := uint8((int(a1) + int(a2) + int(a3) + int(a4)) / 1024) + rimg.Set(x, y, color.RGBA{R: mixr, G: mixg, B: mixb, A: mixa}) + } + } + return rimg, w, h } func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4][2]float64, alpha float64) { @@ -21,33 +48,64 @@ func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float if simg.deleted { return } + + bounds := simg.mips[0].Bounds() + w, h := bounds.Dx(), bounds.Dy() + + factor := float64(w*h) / (sw * sh) + area := quadArea(pts) * factor + mip := simg.mips[0] + closest := math.MaxFloat64 + mipW, mipH := w, h + for _, img := range simg.mips { + bounds := img.Bounds() + w, h := bounds.Dx(), bounds.Dy() + dist := math.Abs(float64(w*h) - area) + if dist < closest { + closest = dist + mip = img + mipW = w + mipH = h + } + } + + mipScaleX := float64(mipW) / float64(w) + mipScaleY := float64(mipH) / float64(h) + sx *= mipScaleX + sy *= mipScaleY + sw *= mipScaleX + sh *= mipScaleY + b.fillQuad(pts, func(x, y int, sx2, sy2 float64) { imgx := sx + sw*sx2 imgy := sy + sh*sy2 imgxf := math.Floor(imgx) imgyf := math.Floor(imgy) - rx := imgx - imgxf - ry := imgy - imgyf - ca := simg.img.At(int(imgxf), int(imgyf)) - cb := simg.img.At(int(imgxf+1), int(imgyf)) - cc := simg.img.At(int(imgxf), int(imgyf+1)) - cd := simg.img.At(int(imgxf+1), int(imgyf+1)) - ctop := lerp(ca, cb, rx) - cbtm := lerp(cc, cd, rx) - b.Image.Set(x, y, lerp(ctop, cbtm, ry)) + c := mip.At(int(imgxf), int(imgyf)) + b.Image.Set(x, y, c) + + // rx := imgx - imgxf + // ry := imgy - imgyf + // ca := mip.At(int(imgxf), int(imgyf)) + // cb := mip.At(int(imgxf+1), int(imgyf)) + // cc := mip.At(int(imgxf), int(imgyf+1)) + // cd := mip.At(int(imgxf+1), int(imgyf+1)) + // ctop := lerp(ca, cb, rx) + // cbtm := lerp(cc, cd, rx) + // b.Image.Set(x, y, lerp(ctop, cbtm, ry)) }) } func (img *Image) Width() int { - return img.img.Bounds().Dx() + return img.mips[0].Bounds().Dx() } func (img *Image) Height() int { - return img.img.Bounds().Dy() + return img.mips[0].Bounds().Dy() } func (img *Image) Size() (w, h int) { - b := img.img.Bounds() + b := img.mips[0].Bounds() return b.Dx(), b.Dy() } @@ -56,7 +114,16 @@ func (img *Image) Delete() { } func (img *Image) Replace(src image.Image) error { - img.img = src + img.mips = img.mips[:1] + img.mips[0] = src + + bounds := src.Bounds() + w, h := bounds.Dx(), bounds.Dy() + for w > 1 && h > 1 { + src, w, h = halveImage(src) + img.mips = append(img.mips, src) + } + return nil } diff --git a/backend/softwarebackend/triangles.go b/backend/softwarebackend/triangles.go index ede37dc..ce5fda7 100644 --- a/backend/softwarebackend/triangles.go +++ b/backend/softwarebackend/triangles.go @@ -88,6 +88,12 @@ func (b *SoftwareBackend) fillTriangle(tri [][2]float64, fn func(x, y int)) { } } +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]} + 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)) { 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])))) diff --git a/canvas_test.go b/canvas_test.go index 23a9832..7714682 100644 --- a/canvas_test.go +++ b/canvas_test.go @@ -13,7 +13,7 @@ import ( "github.com/go-gl/gl/v3.2-core/gl" "github.com/tfriedel6/canvas" - "github.com/tfriedel6/canvas/backend/software" + "github.com/tfriedel6/canvas/backend/softwarebackend" "github.com/tfriedel6/canvas/sdlcanvas" )