diff --git a/backend/software/color.go b/backend/software/color.go new file mode 100644 index 0000000..8f6e3ec --- /dev/null +++ b/backend/software/color.go @@ -0,0 +1,70 @@ +package softwarebackend + +import ( + "image/color" + "math" +) + +func mix(src, dest color.Color) color.RGBA { + ir1, ig1, ib1, ia1 := src.RGBA() + r1 := float64(ir1) / 65535.0 + g1 := float64(ig1) / 65535.0 + b1 := float64(ib1) / 65535.0 + a1 := float64(ia1) / 65535.0 + + ir2, ig2, ib2, _ := dest.RGBA() + r2 := float64(ir2) / 65535.0 + g2 := float64(ig2) / 65535.0 + b2 := float64(ib2) / 65535.0 + + r := (r1-r2)*a1 + r2 + g := (g1-g2)*a1 + g2 + b := (b1-b2)*a1 + b2 + + return color.RGBA{ + R: uint8(math.Round(r * 255.0)), + G: uint8(math.Round(g * 255.0)), + B: uint8(math.Round(b * 255.0)), + A: 255, + } +} + +func alphaColor(col color.Color, alpha color.Alpha) color.RGBA { + ir, ig, ib, _ := col.RGBA() + a2 := float64(alpha.A) / 255.0 + r := float64(ir) * a2 / 65535.0 + g := float64(ig) * a2 / 65535.0 + b := float64(ib) * a2 / 65535.0 + return color.RGBA{ + R: uint8(r * 255.0), + G: uint8(g * 255.0), + B: uint8(b * 255.0), + A: 255, + } +} + +func lerp(col1, col2 color.Color, ratio float64) color.RGBA { + ir1, ig1, ib1, ia1 := col1.RGBA() + r1 := float64(ir1) / 65535.0 + g1 := float64(ig1) / 65535.0 + b1 := float64(ib1) / 65535.0 + a1 := float64(ia1) / 65535.0 + + ir2, ig2, ib2, ia2 := col2.RGBA() + r2 := float64(ir2) / 65535.0 + g2 := float64(ig2) / 65535.0 + b2 := float64(ib2) / 65535.0 + a2 := float64(ia2) / 65535.0 + + r := (r1-r2)*ratio + r2 + g := (g1-g2)*ratio + g2 + b := (b1-b2)*ratio + b2 + a := (a1-a2)*ratio + a2 + + return color.RGBA{ + R: uint8(math.Round(r * 255.0)), + G: uint8(math.Round(g * 255.0)), + B: uint8(math.Round(b * 255.0)), + A: uint8(math.Round(a * 255.0)), + } +} diff --git a/backend/software/fill.go b/backend/software/fill.go index 8a07b1e..ab4acdc 100644 --- a/backend/software/fill.go +++ b/backend/software/fill.go @@ -3,7 +3,6 @@ package softwarebackend import ( "image" "image/color" - "math" "github.com/tfriedel6/canvas/backend/backendbase" ) @@ -39,50 +38,12 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { }) } -func mix(src, dest color.Color) color.RGBA { - ir1, ig1, ib1, ia1 := src.RGBA() - r1 := float64(ir1) / 65535.0 - g1 := float64(ig1) / 65535.0 - b1 := float64(ib1) / 65535.0 - a1 := float64(ia1) / 65535.0 - - ir2, ig2, ib2, _ := dest.RGBA() - r2 := float64(ir2) / 65535.0 - g2 := float64(ig2) / 65535.0 - b2 := float64(ib2) / 65535.0 - - r := (r1-r2)*a1 + r2 - g := (g1-g2)*a1 + g2 - b := (b1-b2)*a1 + b2 - - return color.RGBA{ - R: uint8(math.Round(r * 255.0)), - G: uint8(math.Round(g * 255.0)), - B: uint8(math.Round(b * 255.0)), - A: 255, - } -} - -func alphaColor(col color.Color, alpha color.Alpha) color.RGBA { - ir, ig, ib, _ := col.RGBA() - a2 := float64(alpha.A) / 255.0 - r := float64(ir) * a2 / 65535.0 - g := float64(ig) * a2 / 65535.0 - b := float64(ib) * a2 / 65535.0 - return color.RGBA{ - R: uint8(r * 255.0), - G: uint8(g * 255.0), - B: uint8(b * 255.0), - A: 255, - } -} - func (b *SoftwareBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [4][2]float64) { mw := float64(mask.Bounds().Dx()) mh := float64(mask.Bounds().Dy()) b.fillQuad(pts, func(x, y int, sx2, sy2 float64) { - sxi := int(mw*sx2 + 0.5) - syi := int(mh*sy2 + 0.5) + sxi := int(mw * sx2) + syi := int(mh * sy2) a := mask.AlphaAt(sxi, syi) if a.A == 0 { return diff --git a/backend/software/images.go b/backend/software/images.go index 287949a..6fea22d 100644 --- a/backend/software/images.go +++ b/backend/software/images.go @@ -2,6 +2,7 @@ package softwarebackend import ( "image" + "math" "github.com/tfriedel6/canvas/backend/backendbase" ) @@ -21,9 +22,19 @@ func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float 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)) + 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)) }) } diff --git a/backend/software/triangles.go b/backend/software/triangles.go index 476ff31..a0b2786 100644 --- a/backend/software/triangles.go +++ b/backend/software/triangles.go @@ -134,27 +134,25 @@ func (b *SoftwareBackend) fillQuad(quad [4][2]float64, fn func(x, y int, sx, sy continue } - 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 - + sfy := float64(y) + 0.5 - quad[0][1] fl, cr := int(math.Floor(l)), int(math.Ceil(r)) for x := fl; x <= cr; x++ { fx := float64(x) + 0.5 if fx < l || fx >= r { continue } - fn(x, y, sx, sy) - sx += sxStep - sy += syStep + sfx := fx - quad[0][0] + + var sx, sy float64 + if math.Abs(leftv[0]) > math.Abs(leftv[1]) { + sx = (sfy - sfx*(leftv[1]/leftv[0])) / (topv[1] - topv[0]*(leftv[1]/leftv[0])) + sy = (sfx - topv[0]*sx) / leftv[0] + } else { + sx = (sfx - sfy*(leftv[0]/leftv[1])) / (topv[0] - topv[1]*(leftv[0]/leftv[1])) + sy = (sfy - topv[1]*sx) / leftv[1] + } + + fn(x, y, sx/topLen, sy/leftLen) } } }