implemented mipmapping
This commit is contained in:
parent
bbcb712b73
commit
4e5c38b295
3 changed files with 89 additions and 16 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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]))))
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue