diff --git a/backend/softwarebackend/fill.go b/backend/softwarebackend/fill.go index bc56088..e86c209 100644 --- a/backend/softwarebackend/fill.go +++ b/backend/softwarebackend/fill.go @@ -5,7 +5,7 @@ import ( "image/color" "math" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) func (b *SoftwareBackend) Clear(pts [4]backendbase.Vec) { @@ -19,7 +19,12 @@ func (b *SoftwareBackend) Clear(pts [4]backendbase.Vec) { }) } -func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, tf backendbase.Mat, canOverlap bool) { +func (b *SoftwareBackend) Fill( + style *backendbase.FillStyle, + pts []backendbase.Vec, + tf backendbase.Mat, + canOverlap bool, +) { ffn := fillFunc(style) var triBuf [500]backendbase.Vec @@ -44,7 +49,11 @@ func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts []backendbase.V } } -func (b *SoftwareBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [4]backendbase.Vec) { +func (b *SoftwareBackend) FillImageMask( + style *backendbase.FillStyle, + mask *image.Alpha, + pts [4]backendbase.Vec, +) { ffn := fillFunc(style) mw := float64(mask.Bounds().Dx()) @@ -65,7 +74,10 @@ func fillFunc(style *backendbase.FillStyle) func(x, y float64) color.RGBA { if lg := style.LinearGradient; lg != nil { lg := lg.(*LinearGradient) from := backendbase.Vec{style.Gradient.X0, style.Gradient.Y0} - dir := backendbase.Vec{style.Gradient.X1 - style.Gradient.X0, style.Gradient.Y1 - style.Gradient.Y0} + dir := backendbase.Vec{ + style.Gradient.X1 - style.Gradient.X0, + style.Gradient.Y1 - style.Gradient.Y0, + } dirlen := math.Sqrt(dir[0]*dir[0] + dir[1]*dir[1]) dir[0] /= dirlen dir[1] /= dirlen diff --git a/backend/softwarebackend/images.go b/backend/softwarebackend/images.go index 6375509..dba4a6b 100644 --- a/backend/softwarebackend/images.go +++ b/backend/softwarebackend/images.go @@ -5,7 +5,7 @@ import ( "image/color" "math" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) type Image struct { @@ -43,7 +43,12 @@ func halveImage(img image.Image) (*image.RGBA, int, int) { return rimg, w, h } -func (b *SoftwareBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4]backendbase.Vec, alpha float64) { +func (b *SoftwareBackend) DrawImage( + dimg backendbase.Image, + sx, sy, sw, sh float64, + pts [4]backendbase.Vec, + alpha float64, +) { simg := dimg.(*Image) if simg.deleted { return @@ -130,7 +135,9 @@ type ImagePattern struct { data backendbase.ImagePatternData } -func (b *SoftwareBackend) LoadImagePattern(data backendbase.ImagePatternData) backendbase.ImagePattern { +func (b *SoftwareBackend) LoadImagePattern( + data backendbase.ImagePatternData, +) backendbase.ImagePattern { return &ImagePattern{ data: data, } diff --git a/backend/softwarebackend/software.go b/backend/softwarebackend/software.go index eddc30c..b6e22c8 100644 --- a/backend/softwarebackend/software.go +++ b/backend/softwarebackend/software.go @@ -4,7 +4,7 @@ import ( "image" "image/draw" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) type SoftwareBackend struct { diff --git a/backend/softwarebackend/triangles.go b/backend/softwarebackend/triangles.go index 3d16c09..0ec83ca 100644 --- a/backend/softwarebackend/triangles.go +++ b/backend/softwarebackend/triangles.go @@ -4,7 +4,7 @@ import ( "image/color" "math" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) func triangleLR(tri []backendbase.Vec, y float64) (l, r float64, outside bool) { @@ -96,7 +96,12 @@ type msaaPixel struct { tx, ty float64 } -func (b *SoftwareBackend) fillTriangleMSAA(tri []backendbase.Vec, msaaLevel int, msaaPixels []msaaPixel, fn func(x, y int)) []msaaPixel { +func (b *SoftwareBackend) fillTriangleMSAA( + tri []backendbase.Vec, + msaaLevel int, + msaaPixels []msaaPixel, + fn func(x, y int), +) []msaaPixel { msaaStep := 1.0 / float64(msaaLevel+1) minY := int(math.Floor(math.Min(math.Min(tri[0][1], tri[1][1]), tri[2][1]))) @@ -175,7 +180,10 @@ func (b *SoftwareBackend) fillTriangleMSAA(tri []backendbase.Vec, msaaLevel int, sx := float64(x) + msaaStep*0.5 for stepx := 0; stepx <= msaaLevel; stepx++ { if sx >= l[stepy] && sx < r[stepy] { - msaaPixels = addMSAAPixel(msaaPixels, msaaPixel{ix: x, iy: y, fx: sx, fy: sy}) + msaaPixels = addMSAAPixel( + msaaPixels, + msaaPixel{ix: x, iy: y, fx: sx, fy: sy}, + ) } sx += msaaStep } @@ -203,8 +211,12 @@ func quadArea(quad [4]backendbase.Vec) float64 { } func (b *SoftwareBackend) fillQuadNoAA(quad [4]backendbase.Vec, fn func(x, y int, tx, ty 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])))) + 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 { @@ -272,11 +284,20 @@ func (b *SoftwareBackend) fillQuadNoAA(quad [4]backendbase.Vec, fn func(x, y int } } -func (b *SoftwareBackend) fillQuadMSAA(quad [4]backendbase.Vec, msaaLevel int, msaaPixels []msaaPixel, fn func(x, y int, tx, ty float64)) []msaaPixel { +func (b *SoftwareBackend) fillQuadMSAA( + quad [4]backendbase.Vec, + msaaLevel int, + msaaPixels []msaaPixel, + fn func(x, y int, tx, ty float64), +) []msaaPixel { msaaStep := 1.0 / float64(msaaLevel+1) - 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])))) + 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 { @@ -390,7 +411,17 @@ func (b *SoftwareBackend) fillQuadMSAA(quad [4]backendbase.Vec, msaaLevel int, m ty = (tfy - topv[1]*tx) / leftv[1] } - msaaPixels = addMSAAPixel(msaaPixels, msaaPixel{ix: x, iy: y, fx: sx, fy: sy, tx: tx / topLen, ty: ty / leftLen}) + msaaPixels = addMSAAPixel( + msaaPixels, + msaaPixel{ + ix: x, + iy: y, + fx: sx, + fy: sy, + tx: tx / topLen, + ty: ty / leftLen, + }, + ) } sx += msaaStep } @@ -402,7 +433,10 @@ func (b *SoftwareBackend) fillQuadMSAA(quad [4]backendbase.Vec, msaaLevel int, m return msaaPixels } -func (b *SoftwareBackend) fillQuad(pts [4]backendbase.Vec, fn func(x, y, tx, ty float64) color.RGBA) { +func (b *SoftwareBackend) fillQuad( + pts [4]backendbase.Vec, + fn func(x, y, tx, ty float64) color.RGBA, +) { b.clearStencil() if b.MSAA > 0 { @@ -426,7 +460,8 @@ func (b *SoftwareBackend) fillQuad(pts [4]backendbase.Vec, fn func(x, y, tx, ty samples := (b.MSAA + 1) * (b.MSAA + 1) for i, px := range msaaPixels { - if px.ix < 0 || b.clip.AlphaAt(px.ix, px.iy).A == 0 || b.stencil.AlphaAt(px.ix, px.iy).A > 0 { + if px.ix < 0 || b.clip.AlphaAt(px.ix, px.iy).A == 0 || + b.stencil.AlphaAt(px.ix, px.iy).A > 0 { continue } b.stencil.SetAlpha(px.ix, px.iy, color.Alpha{A: 255}) @@ -489,7 +524,10 @@ func iterateTriangles(pts []backendbase.Vec, fn func(tri []backendbase.Vec)) { } } -func (b *SoftwareBackend) fillTrianglesNoAA(pts []backendbase.Vec, fn func(x, y float64) color.RGBA) { +func (b *SoftwareBackend) fillTrianglesNoAA( + pts []backendbase.Vec, + fn func(x, y float64) color.RGBA, +) { iterateTriangles(pts[:], func(tri []backendbase.Vec) { b.fillTriangleNoAA(tri, func(x, y int) { if b.clip.AlphaAt(x, y).A == 0 { @@ -507,7 +545,11 @@ func (b *SoftwareBackend) fillTrianglesNoAA(pts []backendbase.Vec, fn func(x, y }) } -func (b *SoftwareBackend) fillTrianglesMSAA(pts []backendbase.Vec, msaaLevel int, fn func(x, y float64) color.RGBA) { +func (b *SoftwareBackend) fillTrianglesMSAA( + pts []backendbase.Vec, + msaaLevel int, + fn func(x, y float64) color.RGBA, +) { var msaaPixelBuf [500]msaaPixel msaaPixels := msaaPixelBuf[:0] @@ -530,7 +572,8 @@ func (b *SoftwareBackend) fillTrianglesMSAA(pts []backendbase.Vec, msaaLevel int samples := (msaaLevel + 1) * (msaaLevel + 1) for i, px := range msaaPixels { - if px.ix < 0 || b.clip.AlphaAt(px.ix, px.iy).A == 0 || b.stencil.AlphaAt(px.ix, px.iy).A > 0 { + if px.ix < 0 || b.clip.AlphaAt(px.ix, px.iy).A == 0 || + b.stencil.AlphaAt(px.ix, px.iy).A > 0 { continue } b.stencil.SetAlpha(px.ix, px.iy, color.Alpha{A: 255}) diff --git a/backend/xmobilebackend/clip.go b/backend/xmobilebackend/clip.go deleted file mode 100755 index a6db88e..0000000 --- a/backend/xmobilebackend/clip.go +++ /dev/null @@ -1,77 +0,0 @@ -package xmobilebackend - -import ( - "unsafe" - - "github.com/tfriedel6/canvas/backend/backendbase" - "golang.org/x/mobile/gl" -) - -func (b *XMobileBackend) ClearClip() { - b.activate() - - b.glctx.StencilMask(0xFF) - b.glctx.Clear(gl.STENCIL_BUFFER_BIT) -} - -func (b *XMobileBackend) Clip(pts []backendbase.Vec) { - b.activate() - - b.ptsBuf = b.ptsBuf[:0] - b.ptsBuf = append(b.ptsBuf, - 0, 0, - 0, float32(b.fh), - float32(b.fw), float32(b.fh), - float32(b.fw), 0) - for _, pt := range pts { - b.ptsBuf = append(b.ptsBuf, float32(pt[0]), float32(pt[1])) - } - - mode := gl.Enum(gl.TRIANGLES) - if len(pts) == 4 { - mode = gl.TRIANGLE_FAN - } - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&b.ptsBuf[0]), len(b.ptsBuf)*4), gl.STREAM_DRAW) - b.glctx.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 0) - - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform4f(b.shd.Color, 1, 1, 1, 1) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:]) - b.glctx.Uniform1f(b.shd.GlobalAlpha, 1) - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncSolid) - b.glctx.EnableVertexAttribArray(b.shd.Vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - - b.glctx.ColorMask(false, false, false, false) - - // set bit 2 in the stencil buffer in the given shape - b.glctx.StencilMask(0x04) - b.glctx.StencilFunc(gl.ALWAYS, 4, 0) - b.glctx.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE) - b.glctx.DrawArrays(mode, 4, len(pts)) - - // on entire screen, where neither bit 1 or 2 are set, invert bit 1 - b.glctx.StencilMask(0x02) - b.glctx.StencilFunc(gl.EQUAL, 0, 0x06) - b.glctx.StencilOp(gl.KEEP, gl.INVERT, gl.INVERT) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - - // on entire screen, clear bit 2 - b.glctx.StencilMask(0x04) - b.glctx.StencilFunc(gl.ALWAYS, 0, 0) - b.glctx.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - - b.glctx.DisableVertexAttribArray(b.shd.Vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - - b.glctx.ColorMask(true, true, true, true) - b.glctx.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) - b.glctx.StencilMask(0xFF) - b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) -} diff --git a/backend/xmobilebackend/fill.go b/backend/xmobilebackend/fill.go deleted file mode 100755 index ce78d0d..0000000 --- a/backend/xmobilebackend/fill.go +++ /dev/null @@ -1,339 +0,0 @@ -package xmobilebackend - -import ( - "image" - "math" - "unsafe" - - "github.com/tfriedel6/canvas/backend/backendbase" - "golang.org/x/mobile/gl" -) - -func (b *XMobileBackend) Clear(pts [4]backendbase.Vec) { - b.activate() - - // first check if the four points are aligned to form a nice rectangle, which can be more easily - // cleared using glScissor and glClear - aligned := pts[0][0] == pts[1][0] && pts[2][0] == pts[3][0] && pts[0][1] == pts[3][1] && pts[1][1] == pts[2][1] - if !aligned { - aligned = pts[0][0] == pts[3][0] && pts[1][0] == pts[2][0] && pts[0][1] == pts[1][1] && pts[2][1] == pts[3][1] - } - if aligned { - minX := math.Floor(math.Min(pts[0][0], pts[2][0])) - maxX := math.Ceil(math.Max(pts[0][0], pts[2][0])) - minY := math.Floor(math.Min(pts[0][1], pts[2][1])) - maxY := math.Ceil(math.Max(pts[0][1], pts[2][1])) - b.clearRect(int(minX), int(minY), int(maxX)-int(minX), int(maxY)-int(minY)) - return - } - - data := [8]float32{ - float32(pts[0][0]), float32(pts[0][1]), - float32(pts[1][0]), float32(pts[1][1]), - float32(pts[2][0]), float32(pts[2][1]), - float32(pts[3][0]), float32(pts[3][1])} - - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:]) - b.glctx.Uniform4f(b.shd.Color, 0, 0, 0, 0) - b.glctx.Uniform1f(b.shd.GlobalAlpha, 1) - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncSolid) - - b.glctx.Disable(gl.BLEND) - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&data[0]), len(data)*4), gl.STREAM_DRAW) - - b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) - - b.glctx.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 0) - b.glctx.EnableVertexAttribArray(b.shd.Vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - b.glctx.DisableVertexAttribArray(b.shd.Vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - - b.glctx.StencilFunc(gl.ALWAYS, 0, 0xFF) - - b.glctx.Enable(gl.BLEND) -} - -func (b *XMobileBackend) clearRect(x, y, w, h int) { - b.glctx.Enable(gl.SCISSOR_TEST) - - var box [4]int32 - b.glctx.GetIntegerv(box[:], gl.SCISSOR_BOX) - - b.glctx.Scissor(int32(x), int32(b.h-y-h), int32(w), int32(h)) - b.glctx.ClearColor(0, 0, 0, 0) - b.glctx.Clear(gl.COLOR_BUFFER_BIT) - b.glctx.Scissor(box[0], box[1], box[2], box[3]) - - b.glctx.Disable(gl.SCISSOR_TEST) -} - -func extent(pts []backendbase.Vec) (min, max backendbase.Vec) { - max[0] = -math.MaxFloat64 - max[1] = -math.MaxFloat64 - min[0] = math.MaxFloat64 - min[1] = math.MaxFloat64 - for _, v := range pts { - min[0] = math.Min(min[0], v[0]) - min[1] = math.Min(min[1], v[1]) - max[0] = math.Max(max[0], v[0]) - max[1] = math.Max(max[1], v[1]) - } - return -} - -func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, tf backendbase.Mat, canOverlap bool) { - b.activate() - - if style.Blur > 0 { - b.offscr1.alpha = true - b.enableTextureRenderTarget(&b.offscr1) - b.glctx.ClearColor(0, 0, 0, 0) - b.glctx.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) - } - - b.ptsBuf = b.ptsBuf[:0] - min, max := extent(pts) - b.ptsBuf = append(b.ptsBuf, - float32(min[0]), float32(min[1]), - float32(min[0]), float32(max[1]), - float32(max[0]), float32(max[1]), - float32(max[0]), float32(min[1])) - for _, pt := range pts { - b.ptsBuf = append(b.ptsBuf, float32(pt[0]), float32(pt[1])) - } - - mode := gl.Enum(gl.TRIANGLES) - if len(pts) == 4 { - mode = gl.TRIANGLE_FAN - } - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&b.ptsBuf[0]), len(b.ptsBuf)*4), gl.STREAM_DRAW) - - if !canOverlap || style.Color.A >= 255 { - vertex, _ := b.useShader(style, mat3(tf), false, 0) - - b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) - b.glctx.EnableVertexAttribArray(vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - b.glctx.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 0) - b.glctx.DrawArrays(mode, 4, len(pts)) - b.glctx.DisableVertexAttribArray(vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - b.glctx.StencilFunc(gl.ALWAYS, 0, 0xFF) - } else { - b.glctx.ColorMask(false, false, false, false) - b.glctx.StencilFunc(gl.ALWAYS, 1, 0xFF) - b.glctx.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE) - b.glctx.StencilMask(0x01) - - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform4f(b.shd.Color, 0, 0, 0, 0) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - m3 := mat3(tf) - b.glctx.UniformMatrix3fv(b.shd.Matrix, m3[:]) - b.glctx.Uniform1f(b.shd.GlobalAlpha, 1) - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncSolid) - - b.glctx.EnableVertexAttribArray(b.shd.Vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - b.glctx.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 0) - b.glctx.DrawArrays(mode, 4, len(pts)) - b.glctx.DisableVertexAttribArray(b.shd.Vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - - b.glctx.ColorMask(true, true, true, true) - - b.glctx.StencilFunc(gl.EQUAL, 1, 0xFF) - - vertex, _ := b.useShader(style, mat3identity, false, 0) - b.glctx.EnableVertexAttribArray(vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - b.glctx.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 0) - - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - b.glctx.DisableVertexAttribArray(vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - - b.glctx.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) - b.glctx.StencilFunc(gl.ALWAYS, 0, 0xFF) - - b.glctx.Clear(gl.STENCIL_BUFFER_BIT) - b.glctx.StencilMask(0xFF) - } - - if style.Blur > 0 { - b.drawBlurred(style.Blur, min, max) - } -} - -func (b *XMobileBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [4]backendbase.Vec) { - b.activate() - - w, h := mask.Rect.Dx(), mask.Rect.Dy() - - b.glctx.ActiveTexture(gl.TEXTURE1) - b.glctx.BindTexture(gl.TEXTURE_2D, b.alphaTex) - b.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, mask.Stride, h, gl.ALPHA, gl.UNSIGNED_BYTE, mask.Pix[0:]) - if w < alphaTexSize { - b.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, w, 0, 1, h, gl.ALPHA, gl.UNSIGNED_BYTE, zeroes[0:]) - } - if h < alphaTexSize { - b.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, 0, h, w, 1, gl.ALPHA, gl.UNSIGNED_BYTE, zeroes[0:]) - } - - if style.Blur > 0 { - b.offscr1.alpha = true - b.enableTextureRenderTarget(&b.offscr1) - b.glctx.ClearColor(0, 0, 0, 0) - b.glctx.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) - } - - b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) - - vertex, alphaTexCoord := b.useShader(style, mat3identity, true, 1) - - b.glctx.EnableVertexAttribArray(vertex) - b.glctx.EnableVertexAttribArray(alphaTexCoord) - - tw := float64(w) / alphaTexSize - th := float64(h) / alphaTexSize - var buf [16]float32 - data := buf[:0] - for _, pt := range pts { - data = append(data, float32(math.Round(pt[0])), float32(math.Round(pt[1]))) - } - data = append(data, 0, 0, 0, float32(th), float32(tw), float32(th), float32(tw), 0) - - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&data[0]), len(data)*4), gl.STREAM_DRAW) - - b.glctx.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(alphaTexCoord, 2, gl.FLOAT, false, 0, 8*4) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - - b.glctx.DisableVertexAttribArray(vertex) - b.glctx.DisableVertexAttribArray(alphaTexCoord) - - b.glctx.ActiveTexture(gl.TEXTURE1) - b.glctx.BindTexture(gl.TEXTURE_2D, b.alphaTex) - - b.glctx.StencilFunc(gl.ALWAYS, 0, 0xFF) - - b.glctx.ActiveTexture(gl.TEXTURE0) - - if style.Blur > 0 { - min, max := extent(pts[:]) - b.drawBlurred(style.Blur, min, max) - } -} - -func (b *XMobileBackend) drawBlurred(size float64, min, max backendbase.Vec) { - b.offscr1.alpha = true - b.offscr2.alpha = true - - // calculate box blur size - fsize := math.Max(1, math.Floor(size)) - sizea := int(fsize) - sizeb := sizea - sizec := sizea - if size-fsize > 0.333333333 { - sizeb++ - } - if size-fsize > 0.666666666 { - sizec++ - } - - min[0] -= fsize * 3 - min[1] -= fsize * 3 - max[0] += fsize * 3 - max[1] += fsize * 3 - min[0] = math.Max(0.0, math.Min(b.fw, min[0])) - min[1] = math.Max(0.0, math.Min(b.fh, min[1])) - max[0] = math.Max(0.0, math.Min(b.fw, max[0])) - max[1] = math.Max(0.0, math.Min(b.fh, max[1])) - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.shadowBuf) - data := [16]float32{ - float32(min[0]), float32(min[1]), - float32(min[0]), float32(max[1]), - float32(max[0]), float32(max[1]), - float32(max[0]), float32(min[1]), - float32(min[0] / b.fw), 1 - float32(min[1]/b.fh), - float32(min[0] / b.fw), 1 - float32(max[1]/b.fh), - float32(max[0] / b.fw), 1 - float32(max[1]/b.fh), - float32(max[0] / b.fw), 1 - float32(min[1]/b.fh), - } - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&data[0]), len(data)*4), gl.STREAM_DRAW) - - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform1i(b.shd.Image, 0) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:]) - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncBoxBlur) - - b.glctx.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 8*4) - b.glctx.EnableVertexAttribArray(b.shd.Vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - - b.glctx.Disable(gl.BLEND) - - b.glctx.ActiveTexture(gl.TEXTURE0) - - b.glctx.ClearColor(0, 0, 0, 0) - - b.enableTextureRenderTarget(&b.offscr2) - b.glctx.BindTexture(gl.TEXTURE_2D, b.offscr1.tex) - b.glctx.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) - b.box3(sizea, 0, false) - b.enableTextureRenderTarget(&b.offscr1) - b.glctx.BindTexture(gl.TEXTURE_2D, b.offscr2.tex) - b.box3(sizeb, -0.5, false) - b.enableTextureRenderTarget(&b.offscr2) - b.glctx.BindTexture(gl.TEXTURE_2D, b.offscr1.tex) - b.box3(sizec, 0, false) - b.enableTextureRenderTarget(&b.offscr1) - b.glctx.BindTexture(gl.TEXTURE_2D, b.offscr2.tex) - b.box3(sizea, 0, true) - b.enableTextureRenderTarget(&b.offscr2) - b.glctx.BindTexture(gl.TEXTURE_2D, b.offscr1.tex) - b.box3(sizeb, -0.5, true) - b.glctx.Enable(gl.BLEND) - b.glctx.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - b.disableTextureRenderTarget() - b.glctx.BindTexture(gl.TEXTURE_2D, b.offscr2.tex) - b.box3(sizec, 0, true) - - b.glctx.DisableVertexAttribArray(b.shd.Vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - - b.glctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) -} - -func (b *XMobileBackend) box3(size int, offset float32, vertical bool) { - b.glctx.Uniform1i(b.shd.BoxSize, size) - if vertical { - b.glctx.Uniform1i(b.shd.BoxVertical, 1) - b.glctx.Uniform1f(b.shd.BoxScale, 1/float32(b.fh)) - } else { - b.glctx.Uniform1i(b.shd.BoxVertical, 0) - b.glctx.Uniform1f(b.shd.BoxScale, 1/float32(b.fw)) - } - b.glctx.Uniform1f(b.shd.BoxOffset, offset) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) -} diff --git a/backend/xmobilebackend/gen/gen.go b/backend/xmobilebackend/gen/gen.go deleted file mode 100644 index 9d388e8..0000000 --- a/backend/xmobilebackend/gen/gen.go +++ /dev/null @@ -1,439 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" -) - -func main() { - { // make sure we are in the right directory - dir, err := os.Getwd() - if err != nil { - log.Fatalf("Failed to get working directory: %v", err) - } - d1 := filepath.Base(dir) - d2 := filepath.Base(filepath.Dir(dir)) - if d2 != "backend" || d1 != "xmobilebackend" { - log.Fatalln("This must be run in the backend/xmobilebackend directory") - } - } - - { // delete existing files - fis, err := ioutil.ReadDir(".") - if err != nil { - log.Fatalf("Failed to read current dir: %v", err) - } - - for _, fi := range fis { - if fi.IsDir() || fi.Name() == "shader.go" { - continue - } - err = os.Remove(fi.Name()) - if err != nil { - log.Fatalf("Failed to delete file %s: %v", fi.Name(), err) - } - } - } - - { // copy gogl files - fis, err := ioutil.ReadDir("../goglbackend") - if err != nil { - log.Fatalf("Failed to read dir ../goglbackend: %v", err) - } - - for _, fi := range fis { - if !strings.HasSuffix(fi.Name(), ".go") || fi.Name() == "shader.go" { - continue - } - path := filepath.Join("../goglbackend", fi.Name()) - data, err := ioutil.ReadFile(path) - if err != nil { - log.Fatalf("Failed to read file %s: %v", path, err) - } - - filename, rewritten := rewrite(fi.Name(), string(data)) - - err = ioutil.WriteFile(filename, ([]byte)(rewritten), 0777) - if err != nil { - log.Fatalf("Failed to write file %s: %v", fi.Name(), err) - } - } - } - - err := exec.Command("go", "fmt").Run() - if err != nil { - log.Fatalf("Failed to run go fmt: %v", err) - } -} - -func rewrite(filename, src string) (string, string) { - src = strings.Replace(src, `package goglbackend`, `package xmobilebackend`, 1) - src = strings.Replace(src, `"github.com/tfriedel6/canvas/backend/goglbackend/gl"`, `"golang.org/x/mobile/gl"`, 1) - src = strings.Replace(src, "\tgl.", "\tb.glctx.", -1) - src = strings.Replace(src, "GoGLBackend", "XMobileBackend", -1) - src = strings.Replace(src, "uint32(gl.TRIANGLES)", "gl.Enum(gl.TRIANGLES)", -1) - - src = strings.Replace(src, `func (g *gradient) Delete() {`, - `func (g *gradient) Delete() { - b := g.b`, -1) - src = strings.Replace(src, `func (g *gradient) load(stops backendbase.Gradient) {`, - `func (g *gradient) load(stops backendbase.Gradient) { - b := g.b`, -1) - src = strings.Replace(src, `func (img *Image) Delete() {`, - `func (img *Image) Delete() { - b := img.b`, -1) - src = strings.Replace(src, `func (img *Image) Replace(src image.Image) error {`, - `func (img *Image) Replace(src image.Image) error { - b := img.b`, -1) - - src = strings.Replace(src, `imageBufTex == 0`, `imageBufTex.Value == 0`, -1) - - src = strings.Replace(src, - `loadImage(src image.Image, tex uint32)`, - `loadImage(b *XMobileBackend, src image.Image, tex gl.Texture)`, -1) - src = strings.Replace(src, - `loadImageRGBA(src *image.RGBA, tex uint32)`, - `loadImageRGBA(b *XMobileBackend, src *image.RGBA, tex gl.Texture)`, -1) - src = strings.Replace(src, - `loadImageGray(src *image.Gray, tex uint32)`, - `loadImageGray(b *XMobileBackend, src *image.Gray, tex gl.Texture)`, -1) - src = strings.Replace(src, - `loadImageConverted(src image.Image, tex uint32)`, - `loadImageConverted(b *XMobileBackend, src image.Image, tex gl.Texture)`, -1) - - src = strings.Replace(src, - `func loadShader(vs, fs string, sp *shaderProgram) error {`, - `func loadShader(b *XMobileBackend, vs, fs string, sp *shaderProgram) error { - sp.b = b`, -1) - - src = strings.Replace(src, `func glError() error { - glErr := gl.GetError() -`, `func glError(b *XMobileBackend) error { - glErr := b.glctx.GetError() -`, -1) - src = rewriteCalls(src, "glError", func(params []string) string { - return "glError(b)" - }) - - src = regexp.MustCompile(`[ \t]+tex[ ]+uint32`).ReplaceAllString(src, "\ttex gl.Texture") - - src = rewriteCalls(src, "b.glctx.BufferData", func(params []string) string { - return "b.glctx.BufferData(" + params[0] + ", byteSlice(" + params[2] + ", " + params[1] + "), " + params[3] + ")" - }) - src = rewriteCalls(src, "b.glctx.VertexAttribPointer", func(params []string) string { - params[5] = strings.Replace(params[5], "gl.PtrOffset(", "", 1) - params[5] = strings.Replace(params[5], ")", "", 1) - params[5] = strings.Replace(params[5], "nil", "0", 1) - return "b.glctx.VertexAttribPointer(" + strings.Join(params, ",") + ")" - }) - src = rewriteCalls(src, "b.glctx.DrawArrays", func(params []string) string { - if strings.HasPrefix(params[2], "int32(") { - params[2] = params[2][6 : len(params[2])-1] - } - return "b.glctx.DrawArrays(" + strings.Join(params, ",") + ")" - }) - src = rewriteCalls(src, "b.glctx.Uniform1fv", func(params []string) string { - params[2] = params[2][1 : len(params[2])-3] - return "b.glctx.Uniform1fv(" + params[0] + ", " + params[2] + ")" - }) - src = rewriteCalls(src, "b.glctx.Uniform1i", func(params []string) string { - if strings.HasPrefix(params[1], "int32(") { - params[1] = params[1][6 : len(params[1])-1] - } - return "b.glctx.Uniform1i(" + strings.Join(params, ",") + ")" - }) - src = rewriteCalls(src, "b.glctx.UniformMatrix3fv", func(params []string) string { - return "b.glctx.UniformMatrix3fv(" + params[0] + ", " + params[3][1:len(params[3])-3] + "[:])" - }) - src = rewriteCalls(src, "b.glctx.TexImage2D", func(params []string) string { - params = append(params[:5], params[6:]...) - for i, param := range params { - if strings.HasPrefix(param, "int32(") { - params[i] = param[6 : len(param)-1] - } else if strings.HasPrefix(param, "gl.Ptr(") { - params[i] = param[8:len(param)-2] + ":]" - } - } - return "b.glctx.TexImage2D(" + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "b.glctx.TexSubImage2D", func(params []string) string { - for i, param := range params { - if strings.HasPrefix(param, "int32(") { - params[i] = param[6 : len(param)-1] - } else if strings.HasPrefix(param, "gl.Ptr(") { - params[i] = param[8:len(param)-2] + ":]" - } - } - return "b.glctx.TexSubImage2D(" + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "b.glctx.GetIntegerv", func(params []string) string { - return "b.glctx.GetIntegerv(" + params[1][1:len(params[1])-2] + ":], " + params[0] + ")" - }) - src = rewriteCalls(src, "b.glctx.GenTextures", func(params []string) string { - return params[1][1:] + " = b.glctx.CreateTexture()" - }) - src = rewriteCalls(src, "b.glctx.DeleteTextures", func(params []string) string { - return "b.glctx.DeleteTexture(" + params[1][1:] + ")" - }) - src = rewriteCalls(src, "b.glctx.GenBuffers", func(params []string) string { - return params[1][1:] + " = b.glctx.CreateBuffer()" - }) - src = rewriteCalls(src, "b.glctx.GenFramebuffers", func(params []string) string { - return params[1][1:] + " = b.glctx.CreateFramebuffer()" - }) - src = rewriteCalls(src, "b.glctx.DeleteFramebuffers", func(params []string) string { - return "b.glctx.DeleteFramebuffer(" + params[1][1:] + ")" - }) - src = rewriteCalls(src, "b.glctx.GenRenderbuffers", func(params []string) string { - return params[1][1:] + " = b.glctx.CreateRenderbuffer()" - }) - src = rewriteCalls(src, "b.glctx.DeleteRenderbuffers", func(params []string) string { - return "b.glctx.DeleteRenderbuffer(" + params[1][1:] + ")" - }) - src = rewriteCalls(src, "b.glctx.RenderbufferStorage", func(params []string) string { - for i, param := range params { - if strings.HasPrefix(param, "int32(") { - params[i] = param[6 : len(param)-1] - } - } - return "b.glctx.RenderbufferStorage(" + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "gl.CheckFramebufferStatus", func(params []string) string { - return "b.glctx.CheckFramebufferStatus(" + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "b.glctx.BindFramebuffer", func(params []string) string { - if params[1] == "0" { - params[1] = "gl.Framebuffer{Value: 0}" - } - return "b.glctx.BindFramebuffer(" + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "b.glctx.ReadPixels", func(params []string) string { - for i, param := range params { - if strings.HasPrefix(param, "int32(") { - params[i] = param[6 : len(param)-1] - } else if strings.HasPrefix(param, "gl.Ptr(") { - params[i] = param[8:len(param)-2] + ":]" - } else if len(param) >= 5 && param[:3] == "vp[" { - params[i] = fmt.Sprintf("int(%s)", param) - } - } - return "b.glctx.ReadPixels(" + params[6] + ", " + strings.Join(params[:len(params)-1], ", ") + ")" - }) - src = rewriteCalls(src, "b.glctx.Viewport", func(params []string) string { - for i, param := range params { - if strings.HasPrefix(param, "int32(") { - params[i] = param[6 : len(param)-1] - } - } - return "b.glctx.Viewport(" + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "loadImage", func(params []string) string { - return "loadImage(b, " + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "loadImageRGBA", func(params []string) string { - return "loadImageRGBA(b, " + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "loadImageGray", func(params []string) string { - return "loadImageGray(b, " + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "loadImageConverted", func(params []string) string { - return "loadImageConverted(b, " + strings.Join(params, ", ") + ")" - }) - src = rewriteCalls(src, "loadShader", func(params []string) string { - return "loadShader(b, " + strings.Join(params, ", ") + ")" - }) - src = strings.ReplaceAll(src, "if tex == 0 {", "if tex.Value == 0 {") - - if filename == "gogl.go" { - filename = "xmobile.go" - src = rewriteMain(src) - } else if filename == "shaders.go" { - src = rewriteShaders(src) - } - - return filename, src -} - -func rewriteMain(src string) string { - src = strings.Replace(src, "type GLContext struct {\n", - "type GLContext struct {\n\tglctx gl.Context\n\n", 1) - src = strings.Replace(src, "ctx := &GLContext{\n", - "ctx := &GLContext{\n\t\tglctx: glctx,\n\n", 1) - src = strings.Replace(src, "\tb.glctx.GetError() // clear error state\n", - "\tb := &XMobileBackend{GLContext: ctx}\n\n\tb.glctx.GetError() // clear error state\n\n", 1) - src = strings.Replace(src, "type XMobileBackend struct {\n", - "type XMobileBackend struct {\n", 1) - src = strings.Replace(src, "func NewGLContext() (*GLContext, error) {", - "func NewGLContext(glctx gl.Context) (*GLContext, error) {", 1) - src = strings.Replace(src, "TextureID uint32", "TextureID gl.Texture", 1) - - src = strings.Replace(src, - ` err := gl.Init() - if err != nil { - return nil, err - } - -`, ` var err error - -`, 1) - - src = strings.Replace(src, - `// New returns a new canvas backend. x, y, w, h define the target -// rectangle in the window. ctx is a GLContext created with -// NewGLContext, but can be nil for a default one. It makes sense -// to pass one in when using for example an onscreen and an -// offscreen backend using the same GL context. -`, `// New returns a new canvas backend. x, y, w, h define the target -// rectangle in the window. ctx is a GLContext created with -// NewGLContext -`, 1) - src = strings.Replace(src, - `// NewOffscreen returns a new offscreen canvas backend. w, h define -// the size of the offscreen texture. ctx is a GLContext created -// with NewGLContext, but can be nil for a default one. It makes -// sense to pass one in when using for example an onscreen and an -// offscreen backend using the same GL context. -`, `// NewOffscreen returns a new offscreen canvas backend. w, h define -// the size of the offscreen texture. ctx is a GLContext created -// with NewGLContext -`, 1) - src = strings.Replace(src, - ` if ctx == nil { - var err error - ctx, err = NewGLContext() - if err != nil { - return nil, err - } - } - -`, "", 1) - - src = strings.Replace(src, - ` buf uint32 - shadowBuf uint32 - alphaTex uint32 -`, - ` buf gl.Buffer - shadowBuf gl.Buffer - alphaTex gl.Texture -`, 1) - - src = strings.Replace(src, `imageBufTex uint32`, `imageBufTex gl.Texture`, 1) - - src = strings.Replace(src, - `type offscreenBuffer struct { - tex gl.Texture - w int - h int - renderStencilBuf uint32 - frameBuf uint32 - alpha bool -} -`, - `type offscreenBuffer struct { - tex gl.Texture - w int - h int - renderStencilBuf gl.Renderbuffer - frameBuf gl.Framebuffer - alpha bool -} -`, 1) - - src = src + ` -func byteSlice(ptr unsafe.Pointer, size int) []byte { - var buf []byte - sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) - sh.Cap = size - sh.Len = size - sh.Data = uintptr(ptr) - return buf -} -` - src = strings.Replace(src, "import (\n", - `import ( - "unsafe" - "reflect" - `, 1) - src = strings.Replace(src, "vertexLoc uint32", "vertexLoc gl.Attrib", 1) - src = strings.Replace(src, "alphaTexSlot int32", "alphaTexSlot int", 1) - src = strings.Replace(src, "vertexLoc, alphaTexCoordLoc uint32", "vertexLoc, alphaTexCoordLoc gl.Attrib", 1) - - return src -} - -func rewriteShaders(src string) string { - src = strings.Replace(src, - `package xmobilebackend -`, - `package xmobilebackend - -import ( - "golang.org/x/mobile/gl" -) -`, 1) - - src = strings.Replace(src, "uint32", "gl.Attrib", -1) - src = strings.Replace(src, "int32", "gl.Uniform", -1) - src = strings.Replace(src, "shdFuncSolid gl.Uniform", "shdFuncSolid int", -1) - - return src -} - -func rewriteCalls(src, funcName string, fn func([]string) string) string { - rewritten := "" - pos := 0 - for { - idx := strings.Index(src[pos:], funcName) - if idx == -1 { - rewritten += src[pos:] - break - } - idx += pos - rewritten += src[pos:idx] - parenStart := idx + len(funcName) - if idx > 5 && src[idx-5:idx] == "func " { - rewritten += src[idx:parenStart] - pos = parenStart - continue - } - if src[parenStart] != '(' { - rewritten += src[idx:parenStart] - pos = parenStart - continue - } - - params := make([]string, 0, 10) - - parenDepth := 0 - paramStart := 0 - paramsStr := src[parenStart+1:] - paramloop: - for i, rn := range paramsStr { - switch rn { - case '(': - parenDepth++ - case ')': - parenDepth-- - if parenDepth == -1 { - params = append(params, strings.TrimSpace(paramsStr[paramStart:i])) - pos = parenStart + i + 2 - break paramloop - } - case ',': - params = append(params, strings.TrimSpace(paramsStr[paramStart:i])) - paramStart = i + 1 - } - } - - rewritten += fn(params) - } - - return rewritten -} diff --git a/backend/xmobilebackend/gradients.go b/backend/xmobilebackend/gradients.go deleted file mode 100755 index a3a51ea..0000000 --- a/backend/xmobilebackend/gradients.go +++ /dev/null @@ -1,92 +0,0 @@ -package xmobilebackend - -import ( - "github.com/tfriedel6/canvas/backend/backendbase" - "golang.org/x/mobile/gl" -) - -// LinearGradient is a gradient with any number of -// stops and any number of colors. The gradient will -// be drawn such that each point on the gradient -// will correspond to a straight line -type LinearGradient struct { - gradient -} - -// RadialGradient is a gradient with any number of -// stops and any number of colors. The gradient will -// be drawn such that each point on the gradient -// will correspond to a circle -type RadialGradient struct { - gradient -} - -type gradient struct { - b *XMobileBackend - tex gl.Texture -} - -func (b *XMobileBackend) LoadLinearGradient(data backendbase.Gradient) backendbase.LinearGradient { - b.activate() - - lg := &LinearGradient{ - gradient: gradient{b: b}, - } - lg.tex = b.glctx.CreateTexture() - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, lg.tex) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - lg.load(data) - return lg -} - -func (b *XMobileBackend) LoadRadialGradient(data backendbase.Gradient) backendbase.RadialGradient { - b.activate() - - rg := &RadialGradient{ - gradient: gradient{b: b}, - } - rg.tex = b.glctx.CreateTexture() - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, rg.tex) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - rg.load(data) - return rg -} - -// Delete explicitly deletes the gradient -func (g *gradient) Delete() { - b := g.b - g.b.activate() - - b.glctx.DeleteTexture(g.tex) -} - -func (lg *LinearGradient) Replace(data backendbase.Gradient) { lg.load(data) } -func (rg *RadialGradient) Replace(data backendbase.Gradient) { rg.load(data) } - -func (g *gradient) load(stops backendbase.Gradient) { - b := g.b - g.b.activate() - - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, g.tex) - var pixels [2048 * 4]byte - pp := 0 - for i := 0; i < 2048; i++ { - c := stops.ColorAt(float64(i) / 2047) - pixels[pp] = c.R - pixels[pp+1] = c.G - pixels[pp+2] = c.B - pixels[pp+3] = c.A - pp += 4 - } - - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels[0:]) -} diff --git a/backend/xmobilebackend/imagedata.go b/backend/xmobilebackend/imagedata.go deleted file mode 100755 index effa4d0..0000000 --- a/backend/xmobilebackend/imagedata.go +++ /dev/null @@ -1,102 +0,0 @@ -package xmobilebackend - -import ( - "image" - "image/color" - "unsafe" - - "golang.org/x/mobile/gl" -) - -// GetImageData returns an RGBA image of the current image -func (b *XMobileBackend) GetImageData(x, y, w, h int) *image.RGBA { - b.activate() - - if x < 0 { - w += x - x = 0 - } - if y < 0 { - h += y - y = 0 - } - if w > b.w { - w = b.w - } - if h > b.h { - h = b.h - } - - var vp [4]int32 - b.glctx.GetIntegerv(vp[:], gl.VIEWPORT) - - size := int(vp[2] * vp[3] * 3) - if len(b.imageBuf) < size { - b.imageBuf = make([]byte, size) - } - b.glctx.ReadPixels(b.imageBuf[0:], int(vp[0]), int(vp[1]), int(vp[2]), int(vp[3]), gl.RGB, gl.UNSIGNED_BYTE) - - rgba := image.NewRGBA(image.Rect(x, y, x+w, y+h)) - for cy := y; cy < y+h; cy++ { - bp := (int(vp[3])-h+cy)*int(vp[2])*3 + x*3 - for cx := x; cx < x+w; cx++ { - rgba.SetRGBA(cx, y+h-1-cy, color.RGBA{R: b.imageBuf[bp], G: b.imageBuf[bp+1], B: b.imageBuf[bp+2], A: 255}) - bp += 3 - } - } - return rgba -} - -// PutImageData puts the given image at the given x/y coordinates -func (b *XMobileBackend) PutImageData(img *image.RGBA, x, y int) { - b.activate() - - b.glctx.ActiveTexture(gl.TEXTURE0) - if b.imageBufTex.Value == 0 { - b.imageBufTex = b.glctx.CreateTexture() - b.glctx.BindTexture(gl.TEXTURE_2D, b.imageBufTex) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - } else { - b.glctx.BindTexture(gl.TEXTURE_2D, b.imageBufTex) - } - - w, h := img.Bounds().Dx(), img.Bounds().Dy() - - if img.Stride == img.Bounds().Dx()*4 { - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE, img.Pix[0:]) - } else { - data := make([]uint8, 0, w*h*4) - for cy := 0; cy < h; cy++ { - start := cy * img.Stride - end := start + w*4 - data = append(data, img.Pix[start:end]...) - } - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE, data[0:]) - } - - dx, dy := float32(x), float32(y) - dw, dh := float32(w), float32(h) - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) - data := [16]float32{dx, dy, dx + dw, dy, dx + dw, dy + dh, dx, dy + dh, - 0, 0, 1, 0, 1, 1, 0, 1} - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&data[0]), len(data)*4), gl.STREAM_DRAW) - - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform1i(b.shd.Image, 0) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:]) - b.glctx.Uniform1f(b.shd.GlobalAlpha, 1) - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncImage) - b.glctx.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 8*4) - b.glctx.EnableVertexAttribArray(b.shd.Vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - b.glctx.DisableVertexAttribArray(b.shd.Vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) -} diff --git a/backend/xmobilebackend/images.go b/backend/xmobilebackend/images.go deleted file mode 100755 index 3ad6f55..0000000 --- a/backend/xmobilebackend/images.go +++ /dev/null @@ -1,223 +0,0 @@ -package xmobilebackend - -import ( - "errors" - "image" - "unsafe" - - "github.com/tfriedel6/canvas/backend/backendbase" - "golang.org/x/mobile/gl" -) - -// Image represents a loaded image that can be used in various drawing functions -type Image struct { - b *XMobileBackend - w, h int - tex gl.Texture - flip bool -} - -func (b *XMobileBackend) LoadImage(src image.Image) (backendbase.Image, error) { - b.activate() - - var tex gl.Texture - tex = b.glctx.CreateTexture() - if tex.Value == 0 { - return nil, errors.New("glGenTextures failed") - } - - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, tex) - if src == nil { - return &Image{tex: tex}, nil - } - - img, err := loadImage(b, src, tex) - if err != nil { - return nil, err - } - img.b = b - - return img, nil -} - -func loadImage(b *XMobileBackend, src image.Image, tex gl.Texture) (*Image, error) { - var img *Image - var err error - switch v := src.(type) { - case *image.RGBA: - img, err = loadImageRGBA(b, v, tex) - if err != nil { - return nil, err - } - case image.Image: - img, err = loadImageConverted(b, v, tex) - if err != nil { - return nil, err - } - default: - return nil, errors.New("Unsupported source type") - } - return img, nil -} - -func loadImageRGBA(b *XMobileBackend, src *image.RGBA, tex gl.Texture) (*Image, error) { - img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()} - - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - if err := glError(b); err != nil { - return nil, err - } - if src.Stride == img.w*4 { - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, img.w, img.h, gl.RGBA, gl.UNSIGNED_BYTE, src.Pix[0:]) - } else { - data := make([]uint8, 0, img.w*img.h*4) - for y := 0; y < img.h; y++ { - start := y * src.Stride - end := start + img.w*4 - data = append(data, src.Pix[start:end]...) - } - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, img.w, img.h, gl.RGBA, gl.UNSIGNED_BYTE, data[0:]) - } - if err := glError(b); err != nil { - return nil, err - } - b.glctx.GenerateMipmap(gl.TEXTURE_2D) - if err := glError(b); err != nil { - return nil, err - } - return img, nil -} - -func loadImageConverted(b *XMobileBackend, src image.Image, tex gl.Texture) (*Image, error) { - img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()} - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - if err := glError(b); err != nil { - return nil, err - } - data := make([]uint8, 0, img.w*img.h*4) - for y := 0; y < img.h; y++ { - for x := 0; x < img.w; x++ { - ir, ig, ib, ia := src.At(x, y).RGBA() - r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8) - data = append(data, r, g, b, a) - } - } - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, img.w, img.h, gl.RGBA, gl.UNSIGNED_BYTE, data[0:]) - if err := glError(b); err != nil { - return nil, err - } - b.glctx.GenerateMipmap(gl.TEXTURE_2D) - if err := glError(b); err != nil { - return nil, err - } - return img, nil -} - -// Width returns the width of the image -func (img *Image) Width() int { return img.w } - -// Height returns the height of the image -func (img *Image) Height() int { return img.h } - -// Size returns the width and height of the image -func (img *Image) Size() (int, int) { return img.w, img.h } - -// Delete deletes the image from memory. Any draw calls -// with a deleted image will not do anything -func (img *Image) Delete() { - b := img.b - img.b.activate() - - b.glctx.DeleteTexture(img.tex) -} - -// Replace replaces the image with the new one -func (img *Image) Replace(src image.Image) error { - b := img.b - img.b.activate() - - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, img.tex) - newImg, err := loadImage(b, src, img.tex) - if err != nil { - return err - } - newImg.b = img.b - *img = *newImg - return nil -} - -func (b *XMobileBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4]backendbase.Vec, alpha float64) { - b.activate() - - img := dimg.(*Image) - - sx /= float64(img.w) - sy /= float64(img.h) - sw /= float64(img.w) - sh /= float64(img.h) - - if img.flip { - sy += sh - sh = -sh - } - - var buf [16]float32 - data := buf[:0] - for _, pt := range pts { - data = append(data, float32(pt[0]), float32(pt[1])) - } - data = append(data, - float32(sx), float32(sy), - float32(sx), float32(sy+sh), - float32(sx+sw), float32(sy+sh), - float32(sx+sw), float32(sy), - ) - - b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) - - b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) - b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&data[0]), len(data)*4), gl.STREAM_DRAW) - - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, img.tex) - - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform1i(b.shd.Image, 0) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:]) - b.glctx.Uniform1f(b.shd.GlobalAlpha, float32(alpha)) - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncImage) - b.glctx.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, 0) - b.glctx.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, 8*4) - b.glctx.EnableVertexAttribArray(b.shd.Vertex) - b.glctx.EnableVertexAttribArray(b.shd.TexCoord) - b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - b.glctx.DisableVertexAttribArray(b.shd.Vertex) - b.glctx.DisableVertexAttribArray(b.shd.TexCoord) - - b.glctx.StencilFunc(gl.ALWAYS, 0, 0xFF) -} - -type ImagePattern struct { - b *XMobileBackend - data backendbase.ImagePatternData -} - -func (b *XMobileBackend) LoadImagePattern(data backendbase.ImagePatternData) backendbase.ImagePattern { - return &ImagePattern{ - b: b, - data: data, - } -} - -func (ip *ImagePattern) Delete() {} -func (ip *ImagePattern) Replace(data backendbase.ImagePatternData) { ip.data = data } diff --git a/backend/xmobilebackend/shader.go b/backend/xmobilebackend/shader.go deleted file mode 100755 index 254ed55..0000000 --- a/backend/xmobilebackend/shader.go +++ /dev/null @@ -1,180 +0,0 @@ -package xmobilebackend - -import ( - "errors" - "fmt" - "reflect" - "strings" - "unicode" - "unicode/utf8" - - "golang.org/x/mobile/gl" -) - -type shaderProgram struct { - b *XMobileBackend - ID gl.Program - vs, fs gl.Shader - - attribs map[string]gl.Attrib - uniforms map[string]gl.Uniform -} - -func loadShader(b *XMobileBackend, vs, fs string, sp *shaderProgram) error { - sp.b = b - glError(b) // clear the current error - - // compile vertex shader - { - sp.vs = b.glctx.CreateShader(gl.VERTEX_SHADER) - b.glctx.ShaderSource(sp.vs, vs) - b.glctx.CompileShader(sp.vs) - - status := b.glctx.GetShaderi(sp.vs, gl.COMPILE_STATUS) - if status != gl.TRUE { - clog := b.glctx.GetShaderInfoLog(sp.vs) - b.glctx.DeleteShader(sp.vs) - return fmt.Errorf("failed to compile vertex shader:\n\n%s", clog) - } - if err := glError(b); err != nil { - return fmt.Errorf("gl error after compiling vertex shader: %v", err) - } - } - - // compile fragment shader - { - sp.fs = b.glctx.CreateShader(gl.FRAGMENT_SHADER) - b.glctx.ShaderSource(sp.fs, fs) - b.glctx.CompileShader(sp.fs) - - status := b.glctx.GetShaderi(sp.fs, gl.COMPILE_STATUS) - if status != gl.TRUE { - clog := b.glctx.GetShaderInfoLog(sp.fs) - b.glctx.DeleteShader(sp.fs) - return fmt.Errorf("failed to compile fragment shader:\n\n%s", clog) - } - if err := glError(b); err != nil { - return fmt.Errorf("gl error after compiling fragment shader: %v", err) - } - } - - // link shader program - { - sp.ID = b.glctx.CreateProgram() - b.glctx.AttachShader(sp.ID, sp.vs) - b.glctx.AttachShader(sp.ID, sp.fs) - b.glctx.LinkProgram(sp.ID) - - status := b.glctx.GetProgrami(sp.ID, gl.LINK_STATUS) - if status != gl.TRUE { - clog := b.glctx.GetProgramInfoLog(sp.ID) - b.glctx.DeleteProgram(sp.ID) - b.glctx.DeleteShader(sp.vs) - b.glctx.DeleteShader(sp.fs) - return fmt.Errorf("failed to link shader program:\n\n%s", clog) - } - if err := glError(b); err != nil { - return fmt.Errorf("gl error after linking shader: %v", err) - } - } - - b.glctx.UseProgram(sp.ID) - // load the attributes - count := b.glctx.GetProgrami(sp.ID, gl.ACTIVE_ATTRIBUTES) - sp.attribs = make(map[string]gl.Attrib, int(count)) - for i := 0; i < count; i++ { - name, _, _ := b.glctx.GetActiveAttrib(sp.ID, uint32(i)) - sp.attribs[name] = b.glctx.GetAttribLocation(sp.ID, name) - } - - // load the uniforms - count = b.glctx.GetProgrami(sp.ID, gl.ACTIVE_UNIFORMS) - sp.uniforms = make(map[string]gl.Uniform, int(count)) - for i := 0; i < count; i++ { - name, _, _ := b.glctx.GetActiveUniform(sp.ID, uint32(i)) - sp.uniforms[name] = b.glctx.GetUniformLocation(sp.ID, name) - } - - return nil -} - -func (sp *shaderProgram) use() { - sp.b.glctx.UseProgram(sp.ID) -} - -func (sp *shaderProgram) delete() { - sp.b.glctx.DeleteProgram(sp.ID) - sp.b.glctx.DeleteShader(sp.vs) - sp.b.glctx.DeleteShader(sp.fs) -} - -func (sp *shaderProgram) loadLocations(target interface{}) error { - val := reflect.ValueOf(target) - if val.Kind() != reflect.Ptr { - panic("target must be a pointer to a struct") - } - val = val.Elem() - if val.Kind() != reflect.Struct { - panic("target must be a pointer to a struct") - } - - sp.b.glctx.UseProgram(sp.ID) - - var errs strings.Builder - - for name, loc := range sp.attribs { - field := val.FieldByName(sp.structName(name)) - if field == (reflect.Value{}) { - fmt.Fprintf(&errs, "field for attribute \"%s\" not found; ", name) - } else if field.Type() != reflect.TypeOf(gl.Attrib{}) { - fmt.Fprintf(&errs, "field for attribute \"%s\" must have type gl.Attrib; ", name) - } else { - field.Set(reflect.ValueOf(loc)) - } - } - - for name, loc := range sp.uniforms { - field := val.FieldByName(sp.structName(name)) - if field == (reflect.Value{}) { - fmt.Fprintf(&errs, "field for uniform \"%s\" not found; ", name) - } else if field.Type() != reflect.TypeOf(gl.Uniform{}) { - fmt.Fprintf(&errs, "field for uniform \"%s\" must have type gl.Uniform; ", name) - } else { - field.Set(reflect.ValueOf(loc)) - } - } - - if errs.Len() > 0 { - return errors.New(strings.TrimSpace(errs.String())) - } - return nil -} - -func (sp *shaderProgram) structName(name string) string { - rn, sz := utf8.DecodeRuneInString(name) - name = fmt.Sprintf("%c%s", unicode.ToUpper(rn), name[sz:]) - idx := strings.IndexByte(name, '[') - if idx > 0 { - name = name[:idx] - } - return name -} - -func (sp *shaderProgram) mustLoadLocations(target interface{}) { - err := sp.loadLocations(target) - if err != nil { - panic(err) - } -} - -func (sp *shaderProgram) enableAllVertexAttribArrays() { - for _, loc := range sp.attribs { - sp.b.glctx.EnableVertexAttribArray(loc) - } -} - -func (sp *shaderProgram) disableAllVertexAttribArrays() { - for _, loc := range sp.attribs { - sp.b.glctx.DisableVertexAttribArray(loc) - } -} diff --git a/backend/xmobilebackend/shaders.go b/backend/xmobilebackend/shaders.go deleted file mode 100755 index 9fc8795..0000000 --- a/backend/xmobilebackend/shaders.go +++ /dev/null @@ -1,162 +0,0 @@ -package xmobilebackend - -import ( - "golang.org/x/mobile/gl" -) - -var unifiedVS = ` -attribute vec2 vertex, texCoord; - -uniform vec2 canvasSize; -uniform mat3 matrix; - -varying vec2 v_cp, v_tc; - -void main() { - v_tc = texCoord; - vec3 v = matrix * vec3(vertex.xy, 1.0); - vec2 tf = v.xy / v.z; - v_cp = tf; - vec2 glp = tf * 2.0 / canvasSize - 1.0; - gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0); -} -` -var unifiedFS = ` -#ifdef GL_ES -precision mediump float; -#endif - -varying vec2 v_cp, v_tc; - -uniform int func; - -uniform vec4 color; -uniform float globalAlpha; - -uniform sampler2D gradient; -uniform vec2 from, dir, to; -uniform float len, radFrom, radTo; - -uniform vec2 imageSize; -uniform sampler2D image; -uniform mat3 imageTransform; -uniform vec2 repeat; - -uniform bool useAlphaTex; -uniform sampler2D alphaTex; - -uniform int boxSize; -uniform bool boxVertical; -uniform float boxScale; -uniform float boxOffset; - -bool isNaN(float v) { - return v < 0.0 || 0.0 < v || v == 0.0 ? false : true; -} - -void main() { - vec4 col = color; - - if (func == 5) { - vec4 sum = vec4(0.0); - if (boxVertical) { - vec2 start = v_tc - vec2(0.0, (float(boxSize) * 0.5 + boxOffset) * boxScale); - for (int i=0; i <= boxSize; i++) { - sum += texture2D(image, start + vec2(0.0, float(i) * boxScale)); - } - } else { - vec2 start = v_tc - vec2((float(boxSize) * 0.5 + boxOffset) * boxScale, 0.0); - for (int i=0; i <= boxSize; i++) { - sum += texture2D(image, start + vec2(float(i) * boxScale, 0.0)); - } - } - gl_FragColor = sum / float(boxSize+1); - return; - } - - if (func == 1) { - vec2 v = v_cp - from; - float r = dot(v, dir) / len; - r = clamp(r, 0.0, 1.0); - col = texture2D(gradient, vec2(r, 0.0)); - } else if (func == 2) { - float o_a = 0.5 * sqrt( - pow(-2.0*from.x*from.x+2.0*from.x*to.x+2.0*from.x*v_cp.x-2.0*to.x*v_cp.x-2.0*from.y*from.y+2.0*from.y*to.y+2.0*from.y*v_cp.y-2.0*to.y*v_cp.y+2.0*radFrom*radFrom-2.0*radFrom*radTo, 2.0) - -4.0*(from.x*from.x-2.0*from.x*v_cp.x+v_cp.x*v_cp.x+from.y*from.y-2.0*from.y*v_cp.y+v_cp.y*v_cp.y-radFrom*radFrom) - *(from.x*from.x-2.0*from.x*to.x+to.x*to.x+from.y*from.y-2.0*from.y*to.y+to.y*to.y-radFrom*radFrom+2.0*radFrom*radTo-radTo*radTo) - ); - float o_b = (from.x*from.x-from.x*to.x-from.x*v_cp.x+to.x*v_cp.x+from.y*from.y-from.y*to.y-from.y*v_cp.y+to.y*v_cp.y-radFrom*radFrom+radFrom*radTo); - float o_c = (from.x*from.x-2.0*from.x*to.x+to.x*to.x+from.y*from.y-2.0*from.y*to.y+to.y*to.y-radFrom*radFrom+2.0*radFrom*radTo-radTo*radTo); - float o1 = (-o_a + o_b) / o_c; - float o2 = (o_a + o_b) / o_c; - if (isNaN(o1) && isNaN(o2)) { - gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); - return; - } - float o = max(o1, o2); - o = clamp(o, 0.0, 1.0); - col = texture2D(gradient, vec2(o, 0.0)); - } else if (func == 3) { - vec3 tfpt = vec3(v_cp, 1.0) * imageTransform; - vec2 imgpt = tfpt.xy / imageSize; - col = texture2D(image, mod(imgpt, 1.0)); - if (imgpt.x < 0.0 || imgpt.x > 1.0) { - col *= repeat.x; - } - if (imgpt.y < 0.0 || imgpt.y > 1.0) { - col *= repeat.y; - } - } else if (func == 4) { - col = texture2D(image, v_tc); - } - - if (useAlphaTex) { - col.a *= texture2D(alphaTex, v_tc).a * globalAlpha; - } else { - col.a *= globalAlpha; - } - - gl_FragColor = col; -} -` - -const ( - shdFuncSolid int = iota - shdFuncLinearGradient - shdFuncRadialGradient - shdFuncImagePattern - shdFuncImage - shdFuncBoxBlur -) - -type unifiedShader struct { - shaderProgram - - Vertex gl.Attrib - TexCoord gl.Attrib - - CanvasSize gl.Uniform - Matrix gl.Uniform - Color gl.Uniform - GlobalAlpha gl.Uniform - - Func gl.Uniform - - UseAlphaTex gl.Uniform - AlphaTex gl.Uniform - - Gradient gl.Uniform - From, To, Dir gl.Uniform - Len gl.Uniform - RadFrom, RadTo gl.Uniform - - ImageSize gl.Uniform - Image gl.Uniform - ImageTransform gl.Uniform - Repeat gl.Uniform - - BoxSize gl.Uniform - BoxVertical gl.Uniform - BoxScale gl.Uniform - BoxOffset gl.Uniform -} diff --git a/backend/xmobilebackend/xmobile.go b/backend/xmobilebackend/xmobile.go deleted file mode 100755 index 07e7350..0000000 --- a/backend/xmobilebackend/xmobile.go +++ /dev/null @@ -1,402 +0,0 @@ -package xmobilebackend - -import ( - "fmt" - "reflect" - "unsafe" - - "github.com/tfriedel6/canvas/backend/backendbase" - "golang.org/x/mobile/gl" -) - -const alphaTexSize = 2048 - -var zeroes [alphaTexSize]byte - -// GLContext is a context that contains all the -// shaders and buffers necessary for rendering -type GLContext struct { - glctx gl.Context - - buf gl.Buffer - shadowBuf gl.Buffer - alphaTex gl.Texture - - shd unifiedShader - - offscr1 offscreenBuffer - offscr2 offscreenBuffer - - imageBufTex gl.Texture - imageBuf []byte - - ptsBuf []float32 -} - -// NewGLContext creates all the necessary GL resources, -// like shaders and buffers -func NewGLContext(glctx gl.Context) (*GLContext, error) { - ctx := &GLContext{ - glctx: glctx, - - ptsBuf: make([]float32, 0, 4096), - } - - var err error - - b := &XMobileBackend{GLContext: ctx} - - b.glctx.GetError() // clear error state - - err = loadShader(b, unifiedVS, unifiedFS, &ctx.shd.shaderProgram) - if err != nil { - return nil, err - } - ctx.shd.shaderProgram.mustLoadLocations(&ctx.shd) - if err = glError(b); err != nil { - return nil, err - } - - ctx.buf = b.glctx.CreateBuffer() - if err = glError(b); err != nil { - return nil, err - } - - ctx.shadowBuf = b.glctx.CreateBuffer() - if err = glError(b); err != nil { - return nil, err - } - - b.glctx.ActiveTexture(gl.TEXTURE0) - ctx.alphaTex = b.glctx.CreateTexture() - b.glctx.BindTexture(gl.TEXTURE_2D, ctx.alphaTex) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, alphaTexSize, alphaTexSize, gl.ALPHA, gl.UNSIGNED_BYTE, nil) - // todo should use gl.RED on OpenGL, gl.ALPHA on OpenGL ES - - b.glctx.Enable(gl.BLEND) - b.glctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) - b.glctx.Enable(gl.STENCIL_TEST) - b.glctx.StencilMask(0xFF) - b.glctx.Clear(gl.STENCIL_BUFFER_BIT) - b.glctx.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) - b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) - - b.glctx.Disable(gl.SCISSOR_TEST) - - return ctx, nil -} - -// XMobileBackend is a canvas backend using Go-GL -type XMobileBackend struct { - x, y, w, h int - fx, fy, fw, fh float64 - - *GLContext - - activateFn func() - disableTextureRenderTarget func() -} - -type offscreenBuffer struct { - tex gl.Texture - w int - h int - renderStencilBuf gl.Renderbuffer - frameBuf gl.Framebuffer - alpha bool -} - -// New returns a new canvas backend. x, y, w, h define the target -// rectangle in the window. ctx is a GLContext created with -// NewGLContext -func New(x, y, w, h int, ctx *GLContext) (*XMobileBackend, error) { - b := &XMobileBackend{ - w: w, - h: h, - fw: float64(w), - fh: float64(h), - GLContext: ctx, - } - - b.activateFn = func() { - b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0}) - b.glctx.Viewport(b.x, b.y, b.w, b.h) - // todo reapply clipping since another application may have used the stencil buffer - } - b.disableTextureRenderTarget = func() { - b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0}) - b.glctx.Viewport(b.x, b.y, b.w, b.h) - } - - return b, nil -} - -// XMobileBackendOffscreen is a canvas backend using an offscreen -// texture -type XMobileBackendOffscreen struct { - XMobileBackend - - TextureID gl.Texture - - offscrBuf offscreenBuffer - offscrImg Image -} - -// NewOffscreen returns a new offscreen canvas backend. w, h define -// the size of the offscreen texture. ctx is a GLContext created -// with NewGLContext -func NewOffscreen(w, h int, alpha bool, ctx *GLContext) (*XMobileBackendOffscreen, error) { - b, err := New(0, 0, w, h, ctx) - if err != nil { - return nil, err - } - bo := &XMobileBackendOffscreen{XMobileBackend: *b} - bo.offscrBuf.alpha = alpha - bo.offscrImg.flip = true - - bo.activateFn = func() { - bo.enableTextureRenderTarget(&bo.offscrBuf) - b.glctx.Viewport(0, 0, bo.w, bo.h) - bo.offscrImg.w = bo.offscrBuf.w - bo.offscrImg.h = bo.offscrBuf.h - bo.offscrImg.tex = bo.offscrBuf.tex - bo.TextureID = bo.offscrBuf.tex - } - bo.disableTextureRenderTarget = func() { - bo.enableTextureRenderTarget(&bo.offscrBuf) - } - - return bo, nil -} - -// SetBounds updates the bounds of the canvas. This would -// usually be called for example when the window is resized -func (b *XMobileBackend) SetBounds(x, y, w, h int) { - b.x, b.y = x, y - b.fx, b.fy = float64(x), float64(y) - b.w, b.h = w, h - b.fw, b.fh = float64(w), float64(h) - if b == activeContext { - b.glctx.Viewport(0, 0, b.w, b.h) - b.glctx.Clear(gl.STENCIL_BUFFER_BIT) - } -} - -// SetSize updates the size of the offscreen texture -func (b *XMobileBackendOffscreen) SetSize(w, h int) { - b.XMobileBackend.SetBounds(0, 0, w, h) - b.offscrImg.w = b.offscrBuf.w - b.offscrImg.h = b.offscrBuf.h -} - -// Size returns the size of the window or offscreen -// texture -func (b *XMobileBackend) Size() (int, int) { - return b.w, b.h -} - -func glError(b *XMobileBackend) error { - glErr := b.glctx.GetError() - if glErr != gl.NO_ERROR { - return fmt.Errorf("GL Error: %x", glErr) - } - return nil -} - -// Activate only needs to be called if there is other -// code also using the GL state -func (b *XMobileBackend) Activate() { - b.activate() -} - -var activeContext *XMobileBackend - -func (b *XMobileBackend) activate() { - if activeContext != b { - activeContext = b - b.activateFn() - } -} - -// Delete deletes the offscreen texture. After calling this -// the backend can no longer be used -func (b *XMobileBackendOffscreen) Delete() { - b.glctx.DeleteTexture(b.offscrBuf.tex) - b.glctx.DeleteFramebuffer(b.offscrBuf.frameBuf) - b.glctx.DeleteRenderbuffer(b.offscrBuf.renderStencilBuf) -} - -// CanUseAsImage returns true if the given backend can be -// directly used by this backend to avoid a conversion. -// Used internally -func (b *XMobileBackend) CanUseAsImage(b2 backendbase.Backend) bool { - _, ok := b2.(*XMobileBackendOffscreen) - return ok -} - -// AsImage returns nil, since this backend cannot be directly -// used as an image. Used internally -func (b *XMobileBackend) AsImage() backendbase.Image { - return nil -} - -// AsImage returns an implementation of the Image interface -// that can be used to render this offscreen texture -// directly. Used internally -func (b *XMobileBackendOffscreen) AsImage() backendbase.Image { - return &b.offscrImg -} - -func (b *XMobileBackend) useShader(style *backendbase.FillStyle, tf [9]float32, useAlpha bool, alphaTexSlot int) (vertexLoc, alphaTexCoordLoc gl.Attrib) { - b.glctx.UseProgram(b.shd.ID) - b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) - b.glctx.UniformMatrix3fv(b.shd.Matrix, tf[:]) - if useAlpha { - b.glctx.Uniform1i(b.shd.UseAlphaTex, 1) - b.glctx.Uniform1i(b.shd.AlphaTex, alphaTexSlot) - } else { - b.glctx.Uniform1i(b.shd.UseAlphaTex, 0) - } - b.glctx.Uniform1f(b.shd.GlobalAlpha, float32(style.Color.A)/255) - - if lg := style.LinearGradient; lg != nil { - lg := lg.(*LinearGradient) - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, lg.tex) - from := backendbase.Vec{style.Gradient.X0, style.Gradient.Y0} - to := backendbase.Vec{style.Gradient.X1, style.Gradient.Y1} - dir := to.Sub(from) - length := dir.Len() - dir = dir.Mulf(1 / length) - b.glctx.Uniform2f(b.shd.From, float32(from[0]), float32(from[1])) - b.glctx.Uniform2f(b.shd.Dir, float32(dir[0]), float32(dir[1])) - b.glctx.Uniform1f(b.shd.Len, float32(length)) - b.glctx.Uniform1i(b.shd.Gradient, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncLinearGradient) - return b.shd.Vertex, b.shd.TexCoord - } - if rg := style.RadialGradient; rg != nil { - rg := rg.(*RadialGradient) - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, rg.tex) - b.glctx.Uniform2f(b.shd.From, float32(style.Gradient.X0), float32(style.Gradient.Y0)) - b.glctx.Uniform2f(b.shd.To, float32(style.Gradient.X1), float32(style.Gradient.Y1)) - b.glctx.Uniform1f(b.shd.RadFrom, float32(style.Gradient.RadFrom)) - b.glctx.Uniform1f(b.shd.RadTo, float32(style.Gradient.RadTo)) - b.glctx.Uniform1i(b.shd.Gradient, 0) - b.glctx.Uniform1i(b.shd.Func, shdFuncRadialGradient) - return b.shd.Vertex, b.shd.TexCoord - } - if ip := style.ImagePattern; ip != nil { - ipd := ip.(*ImagePattern).data - img := ipd.Image.(*Image) - b.glctx.ActiveTexture(gl.TEXTURE0) - b.glctx.BindTexture(gl.TEXTURE_2D, img.tex) - b.glctx.Uniform2f(b.shd.ImageSize, float32(img.w), float32(img.h)) - b.glctx.Uniform1i(b.shd.Image, 0) - var f32mat [9]float32 - for i, v := range ipd.Transform { - f32mat[i] = float32(v) - } - b.glctx.UniformMatrix3fv(b.shd.ImageTransform, f32mat[:]) - switch ipd.Repeat { - case backendbase.Repeat: - b.glctx.Uniform2f(b.shd.Repeat, 1, 1) - case backendbase.RepeatX: - b.glctx.Uniform2f(b.shd.Repeat, 1, 0) - case backendbase.RepeatY: - b.glctx.Uniform2f(b.shd.Repeat, 0, 1) - case backendbase.NoRepeat: - b.glctx.Uniform2f(b.shd.Repeat, 0, 0) - } - b.glctx.Uniform1i(b.shd.Func, shdFuncImagePattern) - return b.shd.Vertex, b.shd.TexCoord - } - - cr := float32(style.Color.R) / 255 - cg := float32(style.Color.G) / 255 - cb := float32(style.Color.B) / 255 - ca := float32(style.Color.A) / 255 - b.glctx.Uniform4f(b.shd.Color, cr, cg, cb, ca) - b.glctx.Uniform1f(b.shd.GlobalAlpha, 1) - b.glctx.Uniform1i(b.shd.Func, shdFuncSolid) - return b.shd.Vertex, b.shd.TexCoord -} - -func (b *XMobileBackend) enableTextureRenderTarget(offscr *offscreenBuffer) { - if offscr.w == b.w && offscr.h == b.h { - b.glctx.BindFramebuffer(gl.FRAMEBUFFER, offscr.frameBuf) - return - } - - if b.w == 0 || b.h == 0 { - return - } - - if offscr.w != 0 && offscr.h != 0 { - b.glctx.DeleteTexture(offscr.tex) - b.glctx.DeleteFramebuffer(offscr.frameBuf) - b.glctx.DeleteRenderbuffer(offscr.renderStencilBuf) - } - offscr.w = b.w - offscr.h = b.h - - b.glctx.ActiveTexture(gl.TEXTURE0) - offscr.tex = b.glctx.CreateTexture() - b.glctx.BindTexture(gl.TEXTURE_2D, offscr.tex) - // todo do non-power-of-two textures work everywhere? - if offscr.alpha { - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, b.w, b.h, gl.RGBA, gl.UNSIGNED_BYTE, nil) - } else { - b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, b.w, b.h, gl.RGB, gl.UNSIGNED_BYTE, nil) - } - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - - offscr.frameBuf = b.glctx.CreateFramebuffer() - b.glctx.BindFramebuffer(gl.FRAMEBUFFER, offscr.frameBuf) - - offscr.renderStencilBuf = b.glctx.CreateRenderbuffer() - b.glctx.BindRenderbuffer(gl.RENDERBUFFER, offscr.renderStencilBuf) - b.glctx.RenderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, b.w, b.h) - b.glctx.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, offscr.renderStencilBuf) - - b.glctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, offscr.tex, 0) - - if err := b.glctx.CheckFramebufferStatus(gl.FRAMEBUFFER); err != gl.FRAMEBUFFER_COMPLETE { - // todo this should maybe not panic - panic(fmt.Sprintf("Failed to set up framebuffer for offscreen texture: %x", err)) - } - - b.glctx.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) -} - -func mat3(m backendbase.Mat) (m3 [9]float32) { - m3[0] = float32(m[0]) - m3[1] = float32(m[1]) - m3[2] = 0 - m3[3] = float32(m[2]) - m3[4] = float32(m[3]) - m3[5] = 0 - m3[6] = float32(m[4]) - m3[7] = float32(m[5]) - m3[8] = 1 - return -} - -var mat3identity = [9]float32{1, 0, 0, 0, 1, 0, 0, 0, 1} - -func byteSlice(ptr unsafe.Pointer, size int) []byte { - var buf []byte - sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) - sh.Cap = size - sh.Len = size - sh.Data = uintptr(ptr) - return buf -} diff --git a/canvas.go b/canvas.go index ee30616..12be155 100644 --- a/canvas.go +++ b/canvas.go @@ -9,9 +9,10 @@ import ( "time" "github.com/golang/freetype/truetype" - "github.com/tfriedel6/canvas/backend/backendbase" "golang.org/x/image/font" "golang.org/x/image/math/fixed" + + "git.mstar.dev/mstar/canvas/backend/backendbase" ) //go:generate go run make_shaders.go @@ -460,7 +461,13 @@ func (cv *Canvas) IsPointInStroke(x, y float64) bool { } var triBuf [500]backendbase.Vec - tris := cv.strokeTris(&cv.path, cv.state.transform, cv.state.transform.Invert(), true, triBuf[:0]) + tris := cv.strokeTris( + &cv.path, + cv.state.transform, + cv.state.transform.Invert(), + true, + triBuf[:0], + ) pt := backendbase.Vec{x, y} diff --git a/earcut.go b/earcut.go index bdeeb5e..c1f9128 100644 --- a/earcut.go +++ b/earcut.go @@ -4,7 +4,7 @@ import ( "math" "sort" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) // Go port of https://github.com/mapbox/earcut.hpp @@ -313,7 +313,8 @@ func (ec *earcut) cureLocalIntersections(start *node) *node { b := p.next.next // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) - if !ec.equals(a, b) && ec.intersects(a, p, p.next, b) && ec.locallyInside(a, b) && ec.locallyInside(b, a) { + if !ec.equals(a, b) && ec.intersects(a, p, p.next, b) && ec.locallyInside(a, b) && + ec.locallyInside(b, a) { ec.indices = append(ec.indices, a.i, p.i, b.i) // remove two nodes involved @@ -633,7 +634,8 @@ func (ec *earcut) pointInTriangle(ax, ay, bx, by, cx, cy, px, py float64) bool { // check if a diagonal between two polygon nodes is valid (lies in polygon interior) func (ec *earcut) isValidDiagonal(a, b *node) bool { - return a.next.i != b.i && a.prev.i != b.i && !ec.intersectsPolygon(a, b) && // dones't intersect other edges + return a.next.i != b.i && a.prev.i != b.i && + !ec.intersectsPolygon(a, b) && // dones't intersect other edges ((ec.locallyInside(a, b) && ec.locallyInside(b, a) && ec.middleInside(a, b) && // locally visible (ec.area(a.prev, a, b.prev) != 0.0 || ec.area(a, b.prev, b) != 0.0)) || // does not create opposite-facing sectors (ec.equals(a, b) && ec.area(a.prev, a, a.next) > 0 && ec.area(b.prev, b, b.next) > 0)) // special zero-length case diff --git a/examples/software/software.go b/examples/software/software.go index 62b88b8..555893f 100644 --- a/examples/software/software.go +++ b/examples/software/software.go @@ -5,8 +5,8 @@ import ( "math" "os" - "github.com/tfriedel6/canvas" - "github.com/tfriedel6/canvas/backend/softwarebackend" + "git.mstar.dev/mstar/canvas" + "git.mstar.dev/mstar/canvas/backend/softwarebackend" ) func main() { diff --git a/go.mod b/go.mod index cbd2763..9659df3 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,8 @@ -module github.com/tfriedel6/canvas +module git.mstar.dev/mstar/canvas require ( - github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/veandco/go-sdl2 v0.4.4 - golang.org/x/exp v0.0.0-20200513190911-00229845015e golang.org/x/image v0.0.0-20200801110659-972c09e46d76 - golang.org/x/mobile v0.0.0-20200801112145-973feb4309de - golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 40c66a5..2a49b53 100644 --- a/go.sum +++ b/go.sum @@ -1,46 +1,5 @@ -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 h1:Ac1OEHHkbAZ6EUnJahF0GKcU0FjPc/V8F1DvjhKngFE= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/veandco/go-sdl2 v0.4.4 h1:coOJGftOdvNvGoUIZmm4XD+ZRQF4mg9ZVHmH3/42zFQ= -github.com/veandco/go-sdl2 v0.4.4/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2AB78DPg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/exp v0.0.0-20200513190911-00229845015e h1:rMqLP+9XLy+LdbCXHjJHAmTfXCr93W7oruWA6Hq1Alc= -golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200801110659-972c09e46d76 h1:U7GPaoQyQmX+CBRWXKrvRzWTbd+slqeSh8uARsIyhAw= golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20200801112145-973feb4309de h1:OVJ6QQUBAesB8CZijKDSsXX7xYVtUhrkY0gwMfbi4p4= -golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns= -golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gradients.go b/gradients.go index af815f7..7e7175b 100644 --- a/gradients.go +++ b/gradients.go @@ -4,7 +4,7 @@ import ( "image/color" "runtime" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) // LinearGradient is a gradient with any number of @@ -126,7 +126,11 @@ func (rg *RadialGradient) AddColorStop(pos float64, stopColor ...interface{}) { rg.loaded = false } -func addColorStop(stops backendbase.Gradient, pos float64, stopColor ...interface{}) (backendbase.Gradient, color.RGBA) { +func addColorStop( + stops backendbase.Gradient, + pos float64, + stopColor ...interface{}, +) (backendbase.Gradient, color.RGBA) { c, _ := parseColor(stopColor...) insert := len(stops) for i, stop := range stops { diff --git a/images.go b/images.go index b834d33..dbc9276 100644 --- a/images.go +++ b/images.go @@ -5,12 +5,11 @@ import ( "errors" "fmt" "image" - "io/ioutil" "os" "strings" "time" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) // Image is a type holding information on an image loaded with the LoadImage @@ -52,7 +51,7 @@ func (cv *Canvas) LoadImage(src interface{}) (*Image, error) { case image.Image: srcImg = v case string: - data, err := ioutil.ReadFile(v) + data, err := os.ReadFile(v) if err != nil { return nil, err } @@ -161,9 +160,11 @@ func (img *Image) Replace(src interface{}) error { // loaded image with the same name parameter. // // The coordinates must be one of the following: -// DrawImage("image", dx, dy) -// DrawImage("image", dx, dy, dw, dh) -// DrawImage("image", sx, sy, sw, sh, dx, dy, dw, dh) +// +// DrawImage("image", dx, dy) +// DrawImage("image", dx, dy, dw, dh) +// DrawImage("image", sx, sy, sw, sh, dx, dy, dw, dh) +// // Where dx/dy/dw/dh are the destination coordinates and sx/sy/sw/sh are the // source coordinates func (cv *Canvas) DrawImage(image interface{}, coords ...float64) { diff --git a/path2d.go b/path2d.go index cd6a804..6660030 100644 --- a/path2d.go +++ b/path2d.go @@ -3,7 +3,7 @@ package canvas import ( "math" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) // Path2D is a type that holds a predefined path which can be drawn @@ -135,7 +135,12 @@ func (p *Path2D) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise b p.arc(x, y, radius, startAngle, endAngle, anticlockwise, backendbase.MatIdentity, true) } -func (p *Path2D) arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool, m backendbase.Mat, ident bool) { +func (p *Path2D) arc( + x, y, radius, startAngle, endAngle float64, + anticlockwise bool, + m backendbase.Mat, + ident bool, +) { checkSelfIntersection := len(p.p) > 0 lastWasMove := len(p.p) == 0 || p.p[len(p.p)-1].flags&pathMove != 0 @@ -301,7 +306,10 @@ func (p *Path2D) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) { } // Ellipse (see equivalent function on canvas type) -func (p *Path2D) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, anticlockwise bool) { +func (p *Path2D) Ellipse( + x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, + anticlockwise bool, +) { checkSelfIntersection := len(p.p) > 0 rs, rc := math.Sincos(rotation) diff --git a/paths.go b/paths.go index 9080b07..5b31a52 100644 --- a/paths.go +++ b/paths.go @@ -3,7 +3,7 @@ package canvas import ( "math" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) // BeginPath clears the current path and starts a new one @@ -70,7 +70,10 @@ func (cv *Canvas) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) { // rotation is the rotation of the ellipse in radians, startAngle and endAngle // are angles in radians, and anticlockwise means that the line is added // anticlockwise -func (cv *Canvas) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, anticlockwise bool) { +func (cv *Canvas) Ellipse( + x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, + anticlockwise bool, +) { tf := cv.tf(backendbase.Vec{x, y}) ax, ay := math.Sincos(startAngle) startAngle2 := backendbase.Vec{ay, ax}.MulMat2(cv.state.transform.Mat2()).Atan2() @@ -113,7 +116,13 @@ func (cv *Canvas) strokePath(path *Path2D, tf backendbase.Mat, inv backendbase.M cv.b.Fill(&stl, tris, backendbase.MatIdentity, true) } -func (cv *Canvas) strokeTris(path *Path2D, tf backendbase.Mat, inv backendbase.Mat, doInv bool, target []backendbase.Vec) []backendbase.Vec { +func (cv *Canvas) strokeTris( + path *Path2D, + tf backendbase.Mat, + inv backendbase.Mat, + doInv bool, + target []backendbase.Vec, +) []backendbase.Vec { if len(path.p) == 0 { return target } @@ -180,7 +189,15 @@ func (cv *Canvas) strokeTris(path *Path2D, tf backendbase.Mat, inv backendbase.M } } - target = append(target, lp0.MulMat(tf), lp1.MulMat(tf), lp3.MulMat(tf), lp0.MulMat(tf), lp3.MulMat(tf), lp2.MulMat(tf)) + target = append( + target, + lp0.MulMat(tf), + lp1.MulMat(tf), + lp3.MulMat(tf), + lp0.MulMat(tf), + lp3.MulMat(tf), + lp2.MulMat(tf), + ) if p.flags&pathAttach != 0 && cv.state.lineWidth > 1 { target = cv.lineJoint(p0, p1, p.next, lp0, lp1, lp2, lp3, tf, target) @@ -249,7 +266,11 @@ func (cv *Canvas) applyLineDash(path []pathPoint) []pathPoint { return path2 } -func (cv *Canvas) lineJoint(p0, p1, p2, l0p0, l0p1, l0p2, l0p3 backendbase.Vec, tf backendbase.Mat, tris []backendbase.Vec) []backendbase.Vec { +func (cv *Canvas) lineJoint( + p0, p1, p2, l0p0, l0p1, l0p2, l0p3 backendbase.Vec, + tf backendbase.Mat, + tris []backendbase.Vec, +) []backendbase.Vec { v2 := p1.Sub(p2).Norm() v3 := backendbase.Vec{v2[1], -v2[0]}.Mulf(cv.state.lineWidth * 0.5) @@ -316,7 +337,12 @@ func (cv *Canvas) lineJoint(p0, p1, p2, l0p0, l0p1, l0p2, l0p3 backendbase.Vec, return tris } -func (cv *Canvas) addCircleTris(center backendbase.Vec, radius float64, tf backendbase.Mat, tris []backendbase.Vec) []backendbase.Vec { +func (cv *Canvas) addCircleTris( + center backendbase.Vec, + radius float64, + tf backendbase.Mat, + tris []backendbase.Vec, +) []backendbase.Vec { step := 6 / radius if step > 0.8 { step = 0.8 @@ -338,7 +364,8 @@ func lineIntersection(a0, a1, b0, b1 backendbase.Vec) (backendbase.Vec, float64, va := a1.Sub(a0) vb := b1.Sub(b0) - if (va[0] == 0 && vb[0] == 0) || (va[1] == 0 && vb[1] == 0) || (va[0] == 0 && va[1] == 0) || (vb[0] == 0 && vb[1] == 0) { + if (va[0] == 0 && vb[0] == 0) || (va[1] == 0 && vb[1] == 0) || (va[0] == 0 && va[1] == 0) || + (vb[0] == 0 && vb[1] == 0) { return backendbase.Vec{}, float64(math.Inf(1)), float64(math.Inf(1)) } d := va[1]*vb[0] - va[0]*vb[1] @@ -410,7 +437,11 @@ func (cv *Canvas) fillPath(path *Path2D, tf backendbase.Mat) { cv.b.Fill(&stl, tris, tf, false) } -func appendSubPathTriangles(tris []backendbase.Vec, mat backendbase.Mat, path []pathPoint) []backendbase.Vec { +func appendSubPathTriangles( + tris []backendbase.Vec, + mat backendbase.Mat, + path []pathPoint, +) []backendbase.Vec { last := path[len(path)-1] if last.flags&pathIsConvex != 0 { p0, p1 := path[0].pos.MulMat(mat), path[1].pos.MulMat(mat) diff --git a/shadows.go b/shadows.go index 765e023..e037861 100644 --- a/shadows.go +++ b/shadows.go @@ -4,7 +4,7 @@ import ( "image" "math" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) func (cv *Canvas) drawShadow(pts []backendbase.Vec, mask *image.Alpha, canOverlap bool) { diff --git a/text.go b/text.go index 6019688..16f80a0 100644 --- a/text.go +++ b/text.go @@ -5,7 +5,6 @@ import ( "fmt" "image" "image/draw" - "io/ioutil" "math" "os" "time" @@ -13,9 +12,10 @@ import ( "github.com/golang/freetype" "github.com/golang/freetype/truetype" - "github.com/tfriedel6/canvas/backend/backendbase" "golang.org/x/image/font" "golang.org/x/image/math/fixed" + + "git.mstar.dev/mstar/canvas/backend/backendbase" ) // Font is a loaded font that can be passed to the @@ -84,7 +84,7 @@ func (cv *Canvas) LoadFont(src interface{}) (*Font, error) { case *truetype.Font: f = &Font{font: v} case string: - data, err := ioutil.ReadFile(v) + data, err := os.ReadFile(v) if err != nil { return nil, err } @@ -174,7 +174,8 @@ func (cv *Canvas) FillText(str string, x, y float64) { } // make sure textImage is large enough for the rendered string - if textImage == nil || textImage.Bounds().Dx() < strWidth || textImage.Bounds().Dy() < strHeight { + if textImage == nil || textImage.Bounds().Dx() < strWidth || + textImage.Bounds().Dy() < strHeight { var size int for size = 2; size < alphaTexSize; size *= 2 { if size >= strWidth && size >= strHeight { @@ -229,10 +230,27 @@ func (cv *Canvas) FillText(str string, x, y float64) { // render textImage to the screen var pts [4]backendbase.Vec - pts[0] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + y}) - pts[1] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + float64(strHeight)/scale + y}) - pts[2] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + float64(strWidth)/scale + x, float64(textOffset.Y)/scale + float64(strHeight)/scale + y}) - pts[3] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + float64(strWidth)/scale + x, float64(textOffset.Y)/scale + y}) + pts[0] = cv.tf( + backendbase.Vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + y}, + ) + pts[1] = cv.tf( + backendbase.Vec{ + float64(textOffset.X)/scale + x, + float64(textOffset.Y)/scale + float64(strHeight)/scale + y, + }, + ) + pts[2] = cv.tf( + backendbase.Vec{ + float64(textOffset.X)/scale + float64(strWidth)/scale + x, + float64(textOffset.Y)/scale + float64(strHeight)/scale + y, + }, + ) + pts[3] = cv.tf( + backendbase.Vec{ + float64(textOffset.X)/scale + float64(strWidth)/scale + x, + float64(textOffset.Y)/scale + y, + }, + ) mask := textImage.SubImage(image.Rect(0, 0, strWidth, strHeight)).(*image.Alpha) @@ -335,7 +353,12 @@ func (cv *Canvas) StrokeText(str string, x, y float64) { } -func (cv *Canvas) measureTextRendering(str string, x, y *float64, frc *frContext, scale float64) (int, int, image.Point, string) { +func (cv *Canvas) measureTextRendering( + str string, + x, y *float64, + frc *frContext, + scale float64, +) (int, int, image.Point, string) { // measure rendered text size var p fixed.Point26_6 prev, hasPrev := truetype.Index(0), false @@ -423,10 +446,18 @@ func (cv *Canvas) measureTextRendering(str string, x, y *float64, frc *frContext w, h := cv.b.Size() fw, fh := float64(w), float64(h) - p0 := cv.tf(backendbase.Vec{float64(bounds.Min.X)/scale + curX, float64(strMinY)/scale + *y}) - p1 := cv.tf(backendbase.Vec{float64(bounds.Min.X)/scale + curX, float64(strMaxY)/scale + *y}) - p2 := cv.tf(backendbase.Vec{float64(bounds.Max.X)/scale + curX, float64(strMaxY)/scale + *y}) - p3 := cv.tf(backendbase.Vec{float64(bounds.Max.X)/scale + curX, float64(strMinY)/scale + *y}) + p0 := cv.tf( + backendbase.Vec{float64(bounds.Min.X)/scale + curX, float64(strMinY)/scale + *y}, + ) + p1 := cv.tf( + backendbase.Vec{float64(bounds.Min.X)/scale + curX, float64(strMaxY)/scale + *y}, + ) + p2 := cv.tf( + backendbase.Vec{float64(bounds.Max.X)/scale + curX, float64(strMaxY)/scale + *y}, + ) + p3 := cv.tf( + backendbase.Vec{float64(bounds.Max.X)/scale + curX, float64(strMinY)/scale + *y}, + ) inside := (p0[0] >= 0 || p1[0] >= 0 || p2[0] >= 0 || p3[0] >= 0) && (p0[1] >= 0 || p1[1] >= 0 || p2[1] >= 0 || p3[1] >= 0) && (p0[0] < fw || p1[0] < fw || p2[0] < fw || p3[0] < fw) && @@ -628,7 +659,12 @@ func (cv *Canvas) runeTris(rn rune) []backendbase.Vec { } p0, on0 := start, true - path := &Path2D{cv: cv, p: make([]pathPoint, 0, 50), standalone: true, noSelfIntersection: true} + path := &Path2D{ + cv: cv, + p: make([]pathPoint, 0, 50), + standalone: true, + noSelfIntersection: true, + } path.MoveTo(float64(p0.X)*scale, -float64(p0.Y)*scale) for _, p := range others { on := p.Flags&0x01 != 0 diff --git a/triangulation.go b/triangulation.go index 8e9f895..1808970 100644 --- a/triangulation.go +++ b/triangulation.go @@ -4,7 +4,7 @@ import ( "math" "sort" - "github.com/tfriedel6/canvas/backend/backendbase" + "git.mstar.dev/mstar/canvas/backend/backendbase" ) const samePointTolerance = 1e-20 @@ -97,7 +97,11 @@ func polygonContainsPoint(polygon []backendbase.Vec, p backendbase.Vec) bool { return count%2 == 1 } -func triangulatePath(path []pathPoint, mat backendbase.Mat, target []backendbase.Vec) []backendbase.Vec { +func triangulatePath( + path []pathPoint, + mat backendbase.Mat, + target []backendbase.Vec, +) []backendbase.Vec { if path[0].pos == path[len(path)-1].pos { path = path[:len(path)-1] }