diff --git a/canvas/arbitrary_polygon.go b/canvas/arbitrary_polygon.go index aa5283fad2..76b096330f 100644 --- a/canvas/arbitrary_polygon.go +++ b/canvas/arbitrary_polygon.go @@ -10,7 +10,7 @@ import ( var _ fyne.CanvasObject = (*ArbitraryPolygon)(nil) // ArbitraryPolygon describes a colored arbitrary polygon primitive in a Fyne canvas. -// The polygon is defined by a list of vertex positions in clockwise order, specified in absolute coordinates +// The polygon is defined by a list of vertex positions in clockwise order, // relative to the object (top-left is (0,0), bottom-right is (width,height)). // Each corner can have an individually specified rounding radius. // @@ -18,46 +18,46 @@ var _ fyne.CanvasObject = (*ArbitraryPolygon)(nil) type ArbitraryPolygon struct { baseObject - Points []fyne.Position // Vertices in coordinates relative to the object. If NormalizedPoints is true, these are (0.0 to 1.0), otherwise absolute. - NormalizedPoints bool // True if Points are specified in normalized coordinates (0.0 to 1.0) relative to the object's size. - CornerRadii []float32 // Per-corner rounding radius, must match len(Points); missing entries default to 0 + Points []fyne.Position // Vertices in coordinates relative to the object. If NormalizedPoints is true, these are (0.0 to 1.0), otherwise absolute + NormalizedPoints bool // True if Points are specified in normalized coordinates (0.0 to 1.0) relative to the object's size + CornerRadii []float32 // Per-corner rounding radius, must match len(Points), missing entries default to 0 FillColor color.Color // The polygon fill color StrokeColor color.Color // The polygon stroke color StrokeWidth float32 // The stroke width of the polygon } // Hide will set this arbitrary polygon to not be visible -func (r *ArbitraryPolygon) Hide() { - r.baseObject.Hide() +func (p *ArbitraryPolygon) Hide() { + p.baseObject.Hide() - repaint(r) + repaint(p) } // Move the arbitrary polygon to a new position, relative to its parent / canvas -func (r *ArbitraryPolygon) Move(pos fyne.Position) { - if r.Position() == pos { +func (p *ArbitraryPolygon) Move(pos fyne.Position) { + if p.Position() == pos { return } - r.baseObject.Move(pos) + p.baseObject.Move(pos) - repaint(r) + repaint(p) } // Refresh causes this arbitrary polygon to be redrawn with its configured state. -func (r *ArbitraryPolygon) Refresh() { - Refresh(r) +func (p *ArbitraryPolygon) Refresh() { + Refresh(p) } // Resize on an arbitrary polygon updates the new size of this object. -func (r *ArbitraryPolygon) Resize(s fyne.Size) { - if s == r.Size() { +func (p *ArbitraryPolygon) Resize(s fyne.Size) { + if s == p.Size() { return } - r.baseObject.Resize(s) + p.baseObject.Resize(s) - Refresh(r) + Refresh(p) } // NewArbitraryPolygon returns a new ArbitraryPolygon instance diff --git a/canvas/canvas.go b/canvas/canvas.go index 17b63e8212..ae679dd1a9 100644 --- a/canvas/canvas.go +++ b/canvas/canvas.go @@ -13,10 +13,6 @@ const ( // This constant represents the maximum possible corner radius, resulting in a circular appearance. // Since: 2.7 RadiusMaximum float32 = math.MaxFloat32 - - // ArbitraryPolygonVerticesMaximum defines the maximum number of vertices supported by the arbitrary polygon - // Since: 2.8 - ArbitraryPolygonVerticesMaximum = 16 ) // Refresh instructs the containing canvas to refresh the specified obj. diff --git a/internal/driver/mobile/gl/fn.go b/internal/driver/mobile/gl/fn.go index 8ecc94e01a..8fd62810b8 100644 --- a/internal/driver/mobile/gl/fn.go +++ b/internal/driver/mobile/gl/fn.go @@ -73,6 +73,7 @@ const ( glfnUniform1f glfnUniform1fv glfnUniform2f + glfnUniform2fv glfnUniform4f glfnUniform4fv glfnUseProgram diff --git a/internal/driver/mobile/gl/gl.go b/internal/driver/mobile/gl/gl.go index 599402cdf1..08f43e3d8c 100644 --- a/internal/driver/mobile/gl/gl.go +++ b/internal/driver/mobile/gl/gl.go @@ -529,6 +529,18 @@ func (ctx *context) Uniform2f(dst Uniform, v0, v1 float32) { }) } +func (ctx *context) Uniform2fv(dst Uniform, src []float32) { + ctx.enqueue(call{ + args: fnargs{ + fn: glfnUniform2fv, + a0: dst.c(), + a1: uintptr(len(src) / 2), + }, + parg: unsafe.Pointer(&src[0]), + blocking: true, + }) +} + func (ctx *context) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) { ctx.enqueue(call{ args: fnargs{ diff --git a/internal/driver/mobile/gl/interface.go b/internal/driver/mobile/gl/interface.go index cf8f7a8961..fe37aaf43a 100644 --- a/internal/driver/mobile/gl/interface.go +++ b/internal/driver/mobile/gl/interface.go @@ -225,6 +225,11 @@ type Context interface { // http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml Uniform2f(dst Uniform, v0, v1 float32) + // Uniform2fv writes a vec2 uniform array of len(src)/2 elements. + // + // http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml + Uniform2fv(dst Uniform, v []float32) + // Uniform4f writes a vec4 uniform variable. // // http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml diff --git a/internal/driver/mobile/gl/work.c b/internal/driver/mobile/gl/work.c index 72763a3766..23bfaecc1d 100644 --- a/internal/driver/mobile/gl/work.c +++ b/internal/driver/mobile/gl/work.c @@ -164,6 +164,9 @@ uintptr_t processFn(struct fnargs* args, char* parg) { case glfnUniform2f: glUniform2f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2); break; + case glfnUniform2fv: + glUniform2fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg); + break; case glfnUniform4f: glUniform4f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3, *(GLfloat*)&args->a4); break; diff --git a/internal/driver/mobile/gl/work.h b/internal/driver/mobile/gl/work.h index 645e494d21..1fb6295f94 100644 --- a/internal/driver/mobile/gl/work.h +++ b/internal/driver/mobile/gl/work.h @@ -79,6 +79,7 @@ typedef enum { glfnUniform1f, glfnUniform1fv, glfnUniform2f, + glfnUniform2fv, glfnUniform4f, glfnUniform4fv, glfnUseProgram, diff --git a/internal/driver/mobile/gl/work_windows.go b/internal/driver/mobile/gl/work_windows.go index 47cda30dd8..d324f85d98 100644 --- a/internal/driver/mobile/gl/work_windows.go +++ b/internal/driver/mobile/gl/work_windows.go @@ -260,6 +260,10 @@ var glfnFuncs = [...]func(c call) (ret uintptr){ syscall.SyscallN(glUniform2f.Addr(), c.args.a0, c.args.a1, c.args.a2) return ret }, + glfnUniform2fv: func(c call) (ret uintptr) { + syscall.SyscallN(glUniform2fv.Addr(), c.args.a0, c.args.a1, uintptr(c.parg)) + return + }, glfnUniform4f: func(c call) (ret uintptr) { syscall.SyscallN(glUniform4f.Addr(), c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4) return ret @@ -350,6 +354,7 @@ var ( glUniform1f = libGLESv2.NewProc("glUniform1f") glUniform1fv = libGLESv2.NewProc("glUniform1fv") glUniform2f = libGLESv2.NewProc("glUniform2f") + glUniform2fv = libGLESv2.NewProc("glUniform2fv") glUniform4f = libGLESv2.NewProc("glUniform4f") glUniform4fv = libGLESv2.NewProc("glUniform4fv") glUseProgram = libGLESv2.NewProc("glUseProgram") diff --git a/internal/painter/draw.go b/internal/painter/draw.go index 2a30f8ef2c..460c87ce74 100644 --- a/internal/painter/draw.go +++ b/internal/painter/draw.go @@ -12,7 +12,10 @@ import ( "golang.org/x/image/math/fixed" ) -const quarterCircleControl = 1 - 0.55228 +const ( + quarterCircleControl = 1 - 0.55228 + ArbitraryPolygonVerticesMaximum = 32 +) // DrawArc rasterizes the given arc object into an image. // The scale function is used to understand how many pixels are required per unit of size. @@ -190,13 +193,12 @@ func DrawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, vectorPad float32, s width := int(scale(size.Width + vectorPad*2)) height := int(scale(size.Height + vectorPad*2)) vertices := polygon.Points - radii := polygon.CornerRadii - num := int(fyne.Min(canvas.ArbitraryPolygonVerticesMaximum, float32(len(vertices)))) + numPoints := int(fyne.Min(ArbitraryPolygonVerticesMaximum, float32(len(vertices)))) raw := image.NewRGBA(image.Rect(0, 0, width, height)) scanner := rasterx.NewScannerGV(int(size.Width), int(size.Height), raw, raw.Bounds()) - if num < 3 { + if numPoints < 3 { return raw } @@ -204,12 +206,12 @@ func DrawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, vectorPad float32, s return fyne.Min(fyne.Max(p.X, 0), fyne.Max(size.Width, 0)), fyne.Min(fyne.Max(p.Y, 0), fyne.Max(size.Height, 0)) } - xScaled := make([]float64, num) - yScaled := make([]float64, num) - radius := make([]float32, num) + xScaled := make([]float64, numPoints) + yScaled := make([]float64, numPoints) + cornerRadii := make([]float32, numPoints) - fixedPoints := make([]fyne.Position, num) - for i := 0; i < num; i++ { + fixedPoints := make([]fyne.Position, numPoints) + for i := 0; i < numPoints; i++ { px, py := vertices[i].X, vertices[i].Y if polygon.NormalizedPoints { px, py = px*size.Width, py*size.Height @@ -219,24 +221,23 @@ func DrawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, vectorPad float32, s xScaled[i] = float64(scale(px + vectorPad)) yScaled[i] = float64(scale(py + vectorPad)) - var r float32 - if i < len(radii) { - r = radii[i] + var radius float32 + if i < len(polygon.CornerRadii) { + radius = polygon.CornerRadii[i] } - radius[i] = r + cornerRadii[i] = radius } - radius = GetMaximumCornerRadiusPolygon(fixedPoints, radius) - - radiiScaled := make([]float64, num) - for i := 0; i < num; i++ { - radiiScaled[i] = float64(scale(radius[i])) + cornerRadii = GetMaximumCornerRadii(fixedPoints, cornerRadii) + cornerRadiiScaled := make([]float64, numPoints) + for i := 0; i < numPoints; i++ { + cornerRadiiScaled[i] = float64(scale(cornerRadii[i])) } if polygon.FillColor != nil { filler := rasterx.NewFiller(width, height, scanner) filler.SetColor(polygon.FillColor) - drawArbitraryPolygon(xScaled, yScaled, radiiScaled, filler) + drawArbitraryPolygon(xScaled, yScaled, cornerRadiiScaled, filler) filler.Draw() } @@ -244,7 +245,7 @@ func DrawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, vectorPad float32, s dasher := rasterx.NewDasher(width, height, scanner) dasher.SetColor(polygon.StrokeColor) dasher.SetStroke(fixed.Int26_6(float64(scale(polygon.StrokeWidth))*64), 0, nil, nil, nil, 0, nil, 0) - drawArbitraryPolygon(xScaled, yScaled, radiiScaled, dasher) + drawArbitraryPolygon(xScaled, yScaled, cornerRadiiScaled, dasher) dasher.Draw() } @@ -913,12 +914,12 @@ func NormalizeBezierCurvePoints(startPoint, endPoint fyne.Position, controlPoint return fyne.NewPos(p1x, p1y), fyne.NewPos(p2x, p2y), cp } -// GetMaximumCornerRadiusPolygon computes the maximum possible corner radius for an individual corner, -// considering the specified corner radius, the radii of adjacent corners, and the maximum radii -// allowed for the width and height of the shape. Corner radius may utilize unused capacity from adjacent corners with radius smaller than maximum value. +// GetMaximumCornerRadii calculates the maximum possible corner radii for an arbitrary polygon with given vertices and desired corner radii. +// It ensures that the specified corner radius do not cause overlaps between adjacent corners by calculating the interior angles at each vertex +// and adjusting the radius proportionally if necessary. The function returns a slice of adjusted corner radii that fit within the geometry of the polygon. // -// This is typically used for drawing circular corners in arbitrary polygons with different corner radii. -func GetMaximumCornerRadiusPolygon(points []fyne.Position, radii []float32) []float32 { +// This is typically used for drawing arbitrary polygons with rounded corners, ensuring that the corners do not overlap and the shape remains visually consistent. +func GetMaximumCornerRadii(points []fyne.Position, radii []float32) []float32 { n := len(points) if n < 3 || len(radii) != n { return radii diff --git a/internal/painter/gl/context.go b/internal/painter/gl/context.go index c88c530c16..e132acd0d6 100644 --- a/internal/painter/gl/context.go +++ b/internal/painter/gl/context.go @@ -40,6 +40,7 @@ type context interface { Uniform1f(uniform Uniform, v float32) Uniform1fv(uniform Uniform, v []float32) Uniform2f(uniform Uniform, v0, v1 float32) + Uniform2fv(uniform Uniform, v []float32) Uniform4f(uniform Uniform, v0, v1, v2, v3 float32) UseProgram(program Program) VertexAttribPointerWithOffset(attribute Attribute, size int, typ uint32, normalized bool, stride, offset int) diff --git a/internal/painter/gl/draw.go b/internal/painter/gl/draw.go index f37d61fe2a..87dab8263d 100644 --- a/internal/painter/gl/draw.go +++ b/internal/painter/gl/draw.go @@ -1,7 +1,6 @@ package gl import ( - "fmt" "image/color" "math" @@ -12,7 +11,7 @@ import ( paint "fyne.io/fyne/v2/internal/painter" ) -const edgeSoftness = 1.0 +const edgeSoftness = 0.5 func (p *painter) createBuffer(size int) Buffer { vbo := p.ctx.CreateBuffer() @@ -24,13 +23,6 @@ func (p *painter) createBuffer(size int) Buffer { return vbo } -func (p *painter) defineVertexArray(prog Program, name string, size, stride, offset int) { - vertAttrib := p.ctx.GetAttribLocation(prog, name) - p.ctx.EnableVertexAttribArray(vertAttrib) - p.ctx.VertexAttribPointerWithOffset(vertAttrib, size, float, false, stride*floatSize, offset*floatSize) - p.logError() -} - func (p *painter) drawBlur(b *canvas.Blur, pos fyne.Position, frame fyne.Size) { if b.Radius == 0 { return @@ -73,8 +65,8 @@ func (p *painter) drawBlur(b *canvas.Blur, pos fyne.Position, frame fyne.Size) { p.ctx.UseProgram(p.blurProgram.ref) p.updateBuffer(p.blurProgram.buff, points) - p.defineVertexArray(p.blurProgram.ref, "vert", 3, 5, 0) - p.defineVertexArray(p.blurProgram.ref, "vertTexCoord", 2, 5, 3) + p.UpdateVertexArray(p.blurProgram, "vert", 3, 5, 0) + p.UpdateVertexArray(p.blurProgram, "vertTexCoord", 2, 5, 3) p.ctx.BlendFunc(one, oneMinusSrcAlpha) p.logError() @@ -270,7 +262,7 @@ func (p *painter) drawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, pos fyn edgeSoftnessScaled := roundToPixel(edgeSoftness*p.pixScale, 1.0) p.SetUniform1f(program, "edge_softness", edgeSoftnessScaled) - numPoints := int(fyne.Min(canvas.ArbitraryPolygonVerticesMaximum, float32(len(polygon.Points)))) + numPoints := int(fyne.Min(paint.ArbitraryPolygonVerticesMaximum, float32(len(polygon.Points)))) p.SetUniform1f(program, "vertex_count", float32(numPoints)) size := polygon.Size() @@ -279,7 +271,7 @@ func (p *painter) drawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, pos fyn } fixedPoints := make([]fyne.Position, numPoints) - radii := make([]float32, numPoints) + cornerRadii := make([]float32, numPoints) for i := 0; i < numPoints; i++ { px, py := polygon.Points[i].X, polygon.Points[i].Y @@ -293,19 +285,22 @@ func (p *painter) drawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, pos fyn if i < len(polygon.CornerRadii) { radius = polygon.CornerRadii[i] } - radii[i] = radius + cornerRadii[i] = radius } - scaledRadii := paint.GetMaximumCornerRadiusPolygon(fixedPoints, radii) + cornerRadii = paint.GetMaximumCornerRadii(fixedPoints, cornerRadii) + verticesScaled := make([]float32, numPoints*2) + cornerRadiiScaled := make([]float32, numPoints) for i := 0; i < numPoints; i++ { - pXScaled, pYScaled := roundToPixel(fixedPoints[i].X*p.pixScale, 1.0), roundToPixel(fixedPoints[i].Y*p.pixScale, 1.0) - p.SetUniform2f(program, fmt.Sprintf("vertices[%d]", i), pXScaled, pYScaled) - - radiusScaled := roundToPixel(scaledRadii[i]*p.pixScale, 1.0) - p.SetUniform1f(program, fmt.Sprintf("radii[%d]", i), radiusScaled) + verticesScaled[i*2] = roundToPixel(fixedPoints[i].X*p.pixScale, 1.0) + verticesScaled[i*2+1] = roundToPixel(fixedPoints[i].Y*p.pixScale, 1.0) + cornerRadiiScaled[i] = roundToPixel(cornerRadii[i]*p.pixScale, 1.0) } + p.SetUniform2fv(program, "vertices", verticesScaled) + p.SetUniform1fv(program, "corner_radii", cornerRadiiScaled) + // Colors and Stroke r, g, b, a := getFragmentColor(polygon.FillColor) p.SetUniform4f(program, "fill_color", r, g, b, a) diff --git a/internal/painter/gl/gl_core.go b/internal/painter/gl/gl_core.go index 24230ffe06..fb4a71a725 100644 --- a/internal/painter/gl/gl_core.go +++ b/internal/painter/gl/gl_core.go @@ -79,8 +79,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.program, "text", "alpha", "cornerRadius", "size", "inset") - p.enableAttribArrays(p.program, "vert", "vertTexCoord") p.blurProgram = ProgramState{ ref: p.createProgram("blur"), @@ -88,8 +86,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.blurProgram, "radius", "size", "kernel") - p.enableAttribArrays(p.blurProgram, "vert", "vertTexCoord") p.lineProgram = ProgramState{ ref: p.createProgram("line"), @@ -97,8 +93,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.lineProgram, "feather", "color", "lineWidth") - p.enableAttribArrays(p.lineProgram, "vert", "normal") p.rectangleProgram = ProgramState{ ref: p.createProgram("rectangle"), @@ -106,11 +100,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations( - p.rectangleProgram, - "frame_size", "rect_coords", "stroke_width", "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.rectangleProgram, "vert", "normal") p.roundRectangleProgram = ProgramState{ ref: p.createProgram("round_rectangle"), @@ -118,13 +107,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.roundRectangleProgram, - "frame_size", "rect_coords", - "stroke_width_half", "rect_size_half", - "radius", "edge_softness", - "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.roundRectangleProgram, "vert", "normal") p.polygonProgram = ProgramState{ ref: p.createProgram("polygon"), @@ -132,13 +114,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.polygonProgram, - "frame_size", "rect_coords", "edge_softness", - "outer_radius", "angle", "sides", - "fill_color", "corner_radius", - "stroke_width", "stroke_color", - ) - p.enableAttribArrays(p.polygonProgram, "vert", "normal") p.arcProgram = ProgramState{ ref: p.createProgram("arc"), @@ -146,15 +121,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arcProgram, - "frame_size", "rect_coords", - "inner_radius", "outer_radius", - "start_angle", "end_angle", - "edge_softness", "corner_radius", - "stroke_width", "stroke_color", - "fill_color", - ) - p.enableAttribArrays(p.arcProgram, "vert", "normal") p.bezierCurveProgram = ProgramState{ ref: p.createProgram("bezier_curve"), @@ -162,13 +128,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.bezierCurveProgram, - "frame_size", "rect_coords", "edge_softness", - "start_point", "end_point", "num_control_points", - "control_point1", "control_point2", - "stroke_width_half", "stroke_color", - ) - p.enableAttribArrays(p.bezierCurveProgram, "vert", "normal") p.arbitraryPolygonProgram = ProgramState{ ref: p.createProgram("arbitrary_polygon"), @@ -176,23 +135,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arbitraryPolygonProgram, arbitraryPolygonUniforms()...) - p.enableAttribArrays(p.arbitraryPolygonProgram, "vert", "normal") -} - -func (p *painter) getUniformLocations(pState ProgramState, names ...string) { - for _, name := range names { - u := p.ctx.GetUniformLocation(pState.ref, name) - pState.uniforms[name] = &UniformState{ref: u} - } -} - -func (p *painter) enableAttribArrays(pState ProgramState, names ...string) { - for _, name := range names { - a := p.ctx.GetAttribLocation(pState.ref, name) - p.ctx.EnableVertexAttribArray(a) - pState.attributes[name] = a - } } type coreContext struct{} @@ -388,6 +330,10 @@ func (c *coreContext) Uniform2f(uniform Uniform, v0, v1 float32) { gl.Uniform2f(int32(uniform), v0, v1) } +func (c *coreContext) Uniform2fv(uniform Uniform, v []float32) { + gl.Uniform2fv(int32(uniform), int32(len(v)/2), &v[0]) +} + func (c *coreContext) Uniform4f(uniform Uniform, v0, v1, v2, v3 float32) { gl.Uniform4f(int32(uniform), v0, v1, v2, v3) } diff --git a/internal/painter/gl/gl_es.go b/internal/painter/gl/gl_es.go index 9e522f23ba..188a17c785 100644 --- a/internal/painter/gl/gl_es.go +++ b/internal/painter/gl/gl_es.go @@ -79,8 +79,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.program, "text", "alpha", "cornerRadius", "size", "inset") - p.enableAttribArrays(p.program, "vert", "vertTexCoord") p.blurProgram = ProgramState{ ref: p.createProgram("blur_es"), @@ -88,8 +86,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.blurProgram, "radius", "size", "kernel") - p.enableAttribArrays(p.blurProgram, "vert", "vertTexCoord") p.lineProgram = ProgramState{ ref: p.createProgram("line_es"), @@ -97,8 +93,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.lineProgram, "color", "feather", "lineWidth") - p.enableAttribArrays(p.lineProgram, "vert", "normal") p.rectangleProgram = ProgramState{ ref: p.createProgram("rectangle_es"), @@ -106,11 +100,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations( - p.rectangleProgram, - "frame_size", "rect_coords", "stroke_width", "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.rectangleProgram, "vert", "normal") p.roundRectangleProgram = ProgramState{ ref: p.createProgram("round_rectangle_es"), @@ -118,13 +107,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.roundRectangleProgram, - "frame_size", "rect_coords", - "stroke_width_half", "rect_size_half", - "radius", "edge_softness", - "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.roundRectangleProgram, "vert", "normal") p.polygonProgram = ProgramState{ ref: p.createProgram("polygon_es"), @@ -132,13 +114,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.polygonProgram, - "frame_size", "rect_coords", "edge_softness", - "outer_radius", "angle", "sides", - "fill_color", "corner_radius", - "stroke_width", "stroke_color", - ) - p.enableAttribArrays(p.polygonProgram, "vert", "normal") p.arcProgram = ProgramState{ ref: p.createProgram("arc_es"), @@ -146,15 +121,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arcProgram, - "frame_size", "rect_coords", - "inner_radius", "outer_radius", - "start_angle", "end_angle", - "edge_softness", "corner_radius", - "stroke_width", "stroke_color", - "fill_color", - ) - p.enableAttribArrays(p.arcProgram, "vert", "normal") p.bezierCurveProgram = ProgramState{ ref: p.createProgram("bezier_curve_es"), @@ -162,13 +128,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.bezierCurveProgram, - "frame_size", "rect_coords", "edge_softness", - "start_point", "end_point", "num_control_points", - "control_point1", "control_point2", - "stroke_width_half", "stroke_color", - ) - p.enableAttribArrays(p.bezierCurveProgram, "vert", "normal") p.arbitraryPolygonProgram = ProgramState{ ref: p.createProgram("arbitrary_polygon_es"), @@ -176,23 +135,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arbitraryPolygonProgram, arbitraryPolygonUniforms()...) - p.enableAttribArrays(p.arbitraryPolygonProgram, "vert", "normal") -} - -func (p *painter) getUniformLocations(pState ProgramState, names ...string) { - for _, name := range names { - u := p.ctx.GetUniformLocation(pState.ref, name) - pState.uniforms[name] = &UniformState{ref: u} - } -} - -func (p *painter) enableAttribArrays(pState ProgramState, names ...string) { - for _, name := range names { - a := p.ctx.GetAttribLocation(pState.ref, name) - p.ctx.EnableVertexAttribArray(a) - pState.attributes[name] = a - } } type esContext struct{} @@ -388,6 +330,10 @@ func (c *esContext) Uniform2f(uniform Uniform, v0, v1 float32) { gl.Uniform2f(int32(uniform), v0, v1) } +func (c *esContext) Uniform2fv(uniform Uniform, v []float32) { + gl.Uniform2fv(int32(uniform), int32(len(v)/2), &v[0]) +} + func (c *esContext) Uniform4f(uniform Uniform, v0, v1, v2, v3 float32) { gl.Uniform4f(int32(uniform), v0, v1, v2, v3) } diff --git a/internal/painter/gl/gl_gomobile.go b/internal/painter/gl/gl_gomobile.go index c5f7b5a61f..241b40f713 100644 --- a/internal/painter/gl/gl_gomobile.go +++ b/internal/painter/gl/gl_gomobile.go @@ -75,8 +75,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.program, "text", "alpha", "cornerRadius", "size", "inset") - p.enableAttribArrays(p.program, "vert", "vertTexCoord") p.blurProgram = ProgramState{ ref: p.createProgram("blur_es"), @@ -84,8 +82,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.blurProgram, "radius", "size", "kernel") - p.enableAttribArrays(p.blurProgram, "vert", "vertTexCoord") p.lineProgram = ProgramState{ ref: p.createProgram("line_es"), @@ -93,8 +89,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.lineProgram, "color", "feather", "lineWidth") - p.enableAttribArrays(p.lineProgram, "vert", "normal") p.rectangleProgram = ProgramState{ ref: p.createProgram("rectangle_es"), @@ -102,11 +96,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations( - p.rectangleProgram, - "frame_size", "rect_coords", "stroke_width", "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.rectangleProgram, "vert", "normal") p.roundRectangleProgram = ProgramState{ ref: p.createProgram("round_rectangle_es"), @@ -114,13 +103,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.roundRectangleProgram, - "frame_size", "rect_coords", - "stroke_width_half", "rect_size_half", - "radius", "edge_softness", - "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.roundRectangleProgram, "vert", "normal") p.polygonProgram = ProgramState{ ref: p.createProgram("polygon_es"), @@ -128,13 +110,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.polygonProgram, - "frame_size", "rect_coords", "edge_softness", - "outer_radius", "angle", "sides", - "fill_color", "corner_radius", - "stroke_width", "stroke_color", - ) - p.enableAttribArrays(p.polygonProgram, "vert", "normal") p.arcProgram = ProgramState{ ref: p.createProgram("arc_es"), @@ -142,15 +117,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arcProgram, - "frame_size", "rect_coords", - "inner_radius", "outer_radius", - "start_angle", "end_angle", - "edge_softness", "corner_radius", - "stroke_width", "stroke_color", - "fill_color", - ) - p.enableAttribArrays(p.arcProgram, "vert", "normal") p.bezierCurveProgram = ProgramState{ ref: p.createProgram("bezier_curve_es"), @@ -158,13 +124,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.bezierCurveProgram, - "frame_size", "rect_coords", "edge_softness", - "start_point", "end_point", "num_control_points", - "control_point1", "control_point2", - "stroke_width_half", "stroke_color", - ) - p.enableAttribArrays(p.bezierCurveProgram, "vert", "normal") p.arbitraryPolygonProgram = ProgramState{ ref: p.createProgram("arbitrary_polygon_es"), @@ -172,8 +131,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arbitraryPolygonProgram, arbitraryPolygonUniforms()...) - p.enableAttribArrays(p.arbitraryPolygonProgram, "vert", "normal") compiled = []ProgramState{ p.program, p.blurProgram, @@ -198,21 +155,6 @@ func (p *painter) Init() { p.arbitraryPolygonProgram = compiled[8] } -func (p *painter) getUniformLocations(pState ProgramState, names ...string) { - for _, name := range names { - u := p.ctx.GetUniformLocation(pState.ref, name) - pState.uniforms[name] = &UniformState{ref: u} - } -} - -func (p *painter) enableAttribArrays(pState ProgramState, names ...string) { - for _, name := range names { - a := p.ctx.GetAttribLocation(pState.ref, name) - p.ctx.EnableVertexAttribArray(a) - pState.attributes[name] = a - } -} - type mobileContext struct { glContext gl.Context } @@ -385,6 +327,10 @@ func (c *mobileContext) Uniform2f(uniform Uniform, v0, v1 float32) { c.glContext.Uniform2f(gl.Uniform(uniform), v0, v1) } +func (c *mobileContext) Uniform2fv(uniform Uniform, v []float32) { + c.glContext.Uniform2fv(gl.Uniform(uniform), v) +} + func (c *mobileContext) Uniform4f(uniform Uniform, v0, v1, v2, v3 float32) { c.glContext.Uniform4f(gl.Uniform(uniform), v0, v1, v2, v3) } diff --git a/internal/painter/gl/gl_wasm.go b/internal/painter/gl/gl_wasm.go index 69f765b736..643316090f 100644 --- a/internal/painter/gl/gl_wasm.go +++ b/internal/painter/gl/gl_wasm.go @@ -69,8 +69,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.program, "text", "alpha", "cornerRadius", "size", "inset") - p.enableAttribArrays(p.program, "vert", "vertTexCoord") p.blurProgram = ProgramState{ ref: p.createProgram("blur_es"), @@ -78,8 +76,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.blurProgram, "radius", "size", "kernel") - p.enableAttribArrays(p.blurProgram, "vert", "vertTexCoord") p.lineProgram = ProgramState{ ref: p.createProgram("line_es"), @@ -87,8 +83,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.lineProgram, "color", "feather", "lineWidth") - p.enableAttribArrays(p.lineProgram, "vert", "normal") p.rectangleProgram = ProgramState{ ref: p.createProgram("rectangle_es"), @@ -96,11 +90,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations( - p.rectangleProgram, - "frame_size", "rect_coords", "stroke_width", "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.rectangleProgram, "vert", "normal") p.roundRectangleProgram = ProgramState{ ref: p.createProgram("round_rectangle_es"), @@ -108,13 +97,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.roundRectangleProgram, - "frame_size", "rect_coords", - "stroke_width_half", "rect_size_half", - "radius", "edge_softness", - "fill_color", "stroke_color", - ) - p.enableAttribArrays(p.roundRectangleProgram, "vert", "normal") p.polygonProgram = ProgramState{ ref: p.createProgram("polygon_es"), @@ -122,13 +104,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.polygonProgram, - "frame_size", "rect_coords", "edge_softness", - "outer_radius", "angle", "sides", - "fill_color", "corner_radius", - "stroke_width", "stroke_color", - ) - p.enableAttribArrays(p.polygonProgram, "vert", "normal") p.arcProgram = ProgramState{ ref: p.createProgram("arc_es"), @@ -136,15 +111,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arcProgram, - "frame_size", "rect_coords", - "inner_radius", "outer_radius", - "start_angle", "end_angle", - "edge_softness", "corner_radius", - "stroke_width", "stroke_color", - "fill_color", - ) - p.enableAttribArrays(p.arcProgram, "vert", "normal") p.bezierCurveProgram = ProgramState{ ref: p.createProgram("bezier_curve_es"), @@ -152,13 +118,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.bezierCurveProgram, - "frame_size", "rect_coords", "edge_softness", - "start_point", "end_point", "num_control_points", - "control_point1", "control_point2", - "stroke_width_half", "stroke_color", - ) - p.enableAttribArrays(p.bezierCurveProgram, "vert", "normal") p.arbitraryPolygonProgram = ProgramState{ ref: p.createProgram("arbitrary_polygon_es"), @@ -166,23 +125,6 @@ func (p *painter) Init() { uniforms: make(map[string]*UniformState), attributes: make(map[string]Attribute), } - p.getUniformLocations(p.arbitraryPolygonProgram, arbitraryPolygonUniforms()...) - p.enableAttribArrays(p.arbitraryPolygonProgram, "vert", "normal") -} - -func (p *painter) getUniformLocations(pState ProgramState, names ...string) { - for _, name := range names { - u := p.ctx.GetUniformLocation(pState.ref, name) - pState.uniforms[name] = &UniformState{ref: u} - } -} - -func (p *painter) enableAttribArrays(pState ProgramState, names ...string) { - for _, name := range names { - a := p.ctx.GetAttribLocation(pState.ref, name) - p.ctx.EnableVertexAttribArray(a) - pState.attributes[name] = a - } } type xjsContext struct{} @@ -357,6 +299,10 @@ func (c *xjsContext) Uniform2f(uniform Uniform, v0, v1 float32) { gl.Uniform2f(gl.Uniform(uniform), v0, v1) } +func (c *xjsContext) Uniform2fv(uniform Uniform, v []float32) { + gl.Uniform2fv(gl.Uniform(uniform), v) +} + func (c *xjsContext) Uniform4f(uniform Uniform, v0, v1, v2, v3 float32) { gl.Uniform4f(gl.Uniform(uniform), v0, v1, v2, v3) } diff --git a/internal/painter/gl/painter.go b/internal/painter/gl/painter.go index f0e106e39f..180e26da0a 100644 --- a/internal/painter/gl/painter.go +++ b/internal/painter/gl/painter.go @@ -6,7 +6,6 @@ import ( "image" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/internal" "fyne.io/fyne/v2/internal/driver" "fyne.io/fyne/v2/theme" @@ -71,12 +70,13 @@ type ProgramState struct { } type UniformState struct { - ref Uniform - prev [4]float32 + ref Uniform + prev [4]float32 + prevv []float32 } func (p *painter) SetUniform1f(pState ProgramState, name string, v float32) { - u := pState.uniforms[name] + u := p.getUniformLocation(pState, name) if u.prev[0] == v { return } @@ -84,8 +84,17 @@ func (p *painter) SetUniform1f(pState ProgramState, name string, v float32) { p.ctx.Uniform1f(u.ref, v) } +func (p *painter) SetUniform1fv(pState ProgramState, name string, v []float32) { + u := p.getUniformLocation(pState, name) + if float32SlicesEqual(u.prevv, v) { + return + } + u.prevv = append(u.prevv[:0], v...) + p.ctx.Uniform1fv(u.ref, v) +} + func (p *painter) SetUniform2f(pState ProgramState, name string, v0, v1 float32) { - u := pState.uniforms[name] + u := p.getUniformLocation(pState, name) if u.prev[0] == v0 && u.prev[1] == v1 { return } @@ -94,8 +103,17 @@ func (p *painter) SetUniform2f(pState ProgramState, name string, v0, v1 float32) p.ctx.Uniform2f(u.ref, v0, v1) } +func (p *painter) SetUniform2fv(pState ProgramState, name string, v []float32) { + u := p.getUniformLocation(pState, name) + if float32SlicesEqual(u.prevv, v) { + return + } + u.prevv = append(u.prevv[:0], v...) + p.ctx.Uniform2fv(u.ref, v) +} + func (p *painter) SetUniform4f(pState ProgramState, name string, v0, v1, v2, v3 float32) { - u := pState.uniforms[name] + u := p.getUniformLocation(pState, name) if u.prev[0] == v0 && u.prev[1] == v1 && u.prev[2] == v2 && u.prev[3] == v3 { return } @@ -107,29 +125,12 @@ func (p *painter) SetUniform4f(pState ProgramState, name string, v0, v1, v2, v3 } func (p *painter) UpdateVertexArray(pState ProgramState, name string, size, stride, offset int) { - a := pState.attributes[name] + a := p.enableAttribArray(pState, name) p.ctx.VertexAttribPointerWithOffset(a, size, float, false, stride*floatSize, offset*floatSize) p.logError() } -// arbitraryPolygonUniforms returns all uniform names needed for the arbitrary polygon shader, -// including the array element names for vertices[0..15] and radii[0..15]. -func arbitraryPolygonUniforms() []string { - names := []string{ - "frame_size", "rect_coords", "edge_softness", - "vertex_count", - "fill_color", "stroke_width", "stroke_color", - } - for i := 0; i < canvas.ArbitraryPolygonVerticesMaximum; i++ { - names = append(names, fmt.Sprintf("vertices[%d]", i)) - } - for i := 0; i < canvas.ArbitraryPolygonVerticesMaximum; i++ { - names = append(names, fmt.Sprintf("radii[%d]", i)) - } - return names -} - // Declare conformity to Painter interface var _ Painter = (*painter)(nil) @@ -241,6 +242,39 @@ func (p *painter) createProgram(shaderFilename string) Program { return prog } +func (p *painter) enableAttribArray(pState ProgramState, name string) Attribute { + a, ok := pState.attributes[name] + if !ok { + a = p.ctx.GetAttribLocation(pState.ref, name) + p.ctx.EnableVertexAttribArray(a) + pState.attributes[name] = a + } + + return a +} + +func (p *painter) getUniformLocation(pState ProgramState, name string) *UniformState { + u, ok := pState.uniforms[name] + if !ok { + u = &UniformState{ref: p.ctx.GetUniformLocation(pState.ref, name)} + pState.uniforms[name] = u + } + + return u +} + func (p *painter) logError() { logGLError(p.ctx.GetError) } + +func float32SlicesEqual(a, b []float32) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} diff --git a/internal/painter/gl/shaders/arbitrary_polygon.frag b/internal/painter/gl/shaders/arbitrary_polygon.frag index 4cfd234a1a..b74dafa06f 100644 --- a/internal/painter/gl/shaders/arbitrary_polygon.frag +++ b/internal/painter/gl/shaders/arbitrary_polygon.frag @@ -1,13 +1,13 @@ #version 110 -#define MAX_VERTICES 16 +#define MAX_VERTICES 32 uniform vec2 frame_size; uniform vec4 rect_coords; uniform float edge_softness; uniform vec2 vertices[MAX_VERTICES]; -uniform float radii[MAX_VERTICES]; +uniform float corner_radii[MAX_VERTICES]; uniform float vertex_count; uniform vec4 fill_color; @@ -43,7 +43,7 @@ float arbitrary_polygon_distance(vec2 p, int num) vec2 point1 = vertices[i]; vec2 point2 = vertices[j]; vec2 point3 = vertices[k]; - float radius = radii[j]; + float radius = corner_radii[j]; vec2 pos = p - point2; vec2 a = normalize(point1 - point2); diff --git a/internal/painter/gl/shaders/arbitrary_polygon_es.frag b/internal/painter/gl/shaders/arbitrary_polygon_es.frag index daed22177f..f4ae8448b3 100644 --- a/internal/painter/gl/shaders/arbitrary_polygon_es.frag +++ b/internal/painter/gl/shaders/arbitrary_polygon_es.frag @@ -10,14 +10,14 @@ precision mediump int; precision lowp sampler2D; #endif -#define MAX_VERTICES 16 +#define MAX_VERTICES 32 uniform vec2 frame_size; uniform vec4 rect_coords; uniform float edge_softness; uniform vec2 vertices[MAX_VERTICES]; -uniform float radii[MAX_VERTICES]; +uniform float corner_radii[MAX_VERTICES]; uniform float vertex_count; uniform vec4 fill_color; @@ -51,7 +51,7 @@ float arbitrary_polygon_distance(vec2 p, int num) if (m == num - 2) p_prev2 = vertices[m]; if (m == num - 1) { p_prev1 = vertices[m]; - r_prev = radii[m]; + r_prev = corner_radii[m]; break; } } @@ -121,7 +121,7 @@ float arbitrary_polygon_distance(vec2 p, int num) // Shift values for the next iteration p_prev2 = point2; p_prev1 = point3; - r_prev = radii[k]; + r_prev = corner_radii[k]; } // Phase 2: Distance to straight edge segments between tangent points