Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 63 additions & 63 deletions internal/painter/gl/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ func (p *painter) drawBlur(b *canvas.Blur, pos fyne.Position, frame fyne.Size) {
return
}

// Ensure blurSnapTex exists at the correct size; reallocate only when dimensions change.
if !p.blurSnapTexValid || p.blurSnapW != bw || p.blurSnapH != bh {
if p.blurSnapTexValid {
p.ctx.DeleteTexture(p.blurSnapTex)
// Ensure blurSnap.tex exists at the correct size; reallocate only when dimensions change.
if !p.blurSnap.texValid || p.blurSnap.width != bw || p.blurSnap.height != bh {
if p.blurSnap.texValid {
p.ctx.DeleteTexture(p.blurSnap.tex)
}
// Use ImageScaleSmooth to enable bilinear filtering.
// It ensures smooth interpolation between samples even when the blur radius is massive.
p.blurSnapTex = p.newTexture(canvas.ImageScaleSmooth)
p.blurSnap.tex = p.newTexture(canvas.ImageScaleSmooth)
p.ctx.TexImage2D(texture2D, 0, bw, bh, colorFormatRGBA, unsignedByte, nil)
p.blurSnapTexValid = true
p.blurSnapW = bw
p.blurSnapH = bh
p.blurSnap.texValid = true
p.blurSnap.width = bw
p.blurSnap.height = bh
}

// Cap the kernel samples at 101 (maxKernelRadius = 50.0) per pass to ensure high performance.
Expand All @@ -71,26 +71,26 @@ func (p *painter) drawBlur(b *canvas.Blur, pos fyne.Position, frame fyne.Size) {
}

// Upload kernel as a 1D texture if radius changed.
if !p.blurKernelTexValid || p.blurKernelRadius != kernelRadius {
if !p.blurKernelTexValid {
p.blurKernelTex = p.ctx.CreateTexture()
if !p.blurKernel.texValid || p.blurKernel.radius != kernelRadius {
if !p.blurKernel.texValid {
p.blurKernel.tex = p.ctx.CreateTexture()
}
p.ctx.ActiveTexture(texture1)
p.ctx.BindTexture(texture2D, p.blurKernelTex)
p.ctx.BindTexture(texture2D, p.blurKernel.tex)
p.ctx.TexParameteri(texture2D, textureMinFilter, textureNearest)
p.ctx.TexParameteri(texture2D, textureMagFilter, textureNearest)
p.ctx.TexParameteri(texture2D, textureWrapS, clampToEdge)
p.ctx.TexParameteri(texture2D, textureWrapT, clampToEdge)
p.ctx.TexImage2D(texture2D, 0, len(values), 1, colorFormatRGBA, unsignedByte, kernelToRGBA(values))
p.blurKernelTexValid = true
p.blurKernelRadius = kernelRadius
p.blurKernel.texValid = true
p.blurKernel.radius = kernelRadius
}

// Copy the blur region from the framebuffer directly to the texture on the GPU.
// glCopyTexSubImage2D uses GL coordinates (y=0 at bottom), so convert the canvas-top y.
fbY := p.fbHeight - int(y) - bh
p.ctx.ActiveTexture(texture0)
p.ctx.BindTexture(texture2D, p.blurSnapTex)
p.ctx.BindTexture(texture2D, p.blurSnap.tex)
p.ctx.CopyTexSubImage2D(texture2D, 0, 0, 0, int(x), fbY, bw, bh)
p.logError()

Expand All @@ -100,56 +100,56 @@ func (p *painter) drawBlur(b *canvas.Blur, pos fyne.Position, frame fyne.Size) {
points[4], points[9] = points[9], points[4]
points[14], points[19] = points[19], points[14]

p.ctx.UseProgram(p.blurProgram.ref)
p.updateBuffer(p.blurProgram.buff, points)
p.UpdateVertexArray(p.blurProgram, "vert", 3, 5, 0)
p.UpdateVertexArray(p.blurProgram, "vertTexCoord", 2, 5, 3)
p.ctx.UseProgram(p.programs.blur.ref)
p.updateBuffer(p.programs.blur.buff, points[:])
p.UpdateVertexArray(p.programs.blur, "vert", 3, 5, 0)
p.UpdateVertexArray(p.programs.blur, "vertTexCoord", 2, 5, 3)

p.ctx.BlendFunc(one, oneMinusSrcAlpha)
p.logError()

cornerRadius := fyne.Min(paint.GetMaximumRadius(b.Size()), b.CornerRadius)
p.SetUniform1f(p.blurProgram, "cornerRadius", roundToPixel(cornerRadius*p.pixScale, 1.0))
p.SetUniform2f(p.blurProgram, "size", float32(bw), float32(bh))
p.SetUniform1f(p.programs.blur, "cornerRadius", roundToPixel(cornerRadius*p.pixScale, 1.0))
p.SetUniform2f(p.programs.blur, "size", float32(bw), float32(bh))

p.SetUniform1f(p.blurProgram, "radius", kernelRadius)
p.SetUniform1f(p.blurProgram, "sampleScale", sampleScale)
p.SetUniform1f(p.programs.blur, "radius", kernelRadius)
p.SetUniform1f(p.programs.blur, "sampleScale", sampleScale)

// Bind kernel texture to unit 1.
p.ctx.ActiveTexture(texture1)
p.ctx.BindTexture(texture2D, p.blurKernelTex)
p.ctx.BindTexture(texture2D, p.blurKernel.tex)

// Bind source texture to unit 0.
p.ctx.ActiveTexture(texture0)
p.ctx.BindTexture(texture2D, p.blurSnapTex)
p.ctx.BindTexture(texture2D, p.blurSnap.tex)

// Set sampler uniforms.
p.SetUniform1i(p.blurProgram, "tex", 0)
p.SetUniform1i(p.blurProgram, "kernelTex", 1)
p.SetUniform1i(p.programs.blur, "tex", 0)
p.SetUniform1i(p.programs.blur, "kernelTex", 1)

// Horizontal Blur
// Draw horizontal blur over the background. Use gl: one, gl: zero to replace the screen content.
p.ctx.BlendFunc(one, zero)
p.SetUniform2f(p.blurProgram, "direction", 1.0/float32(bw), 0.0)
p.SetUniform2f(p.programs.blur, "direction", 1.0/float32(bw), 0.0)

p.ctx.DrawArrays(triangleStrip, 0, 4)

// Capture the horizontally-blurred result back into blurSnapTex
// Capture the horizontally-blurred result back into blurSnap.tex
p.ctx.CopyTexSubImage2D(texture2D, 0, 0, 0, int(x), fbY, bw, bh)

// Vertical Blur
// Draw vertical blur using the horizontally-blurred texture.
// Use one, zero since it replaces the exact same rect we just copied from.
p.ctx.BlendFunc(one, zero)
p.SetUniform2f(p.blurProgram, "direction", 0.0, 1.0/float32(bh))
p.SetUniform2f(p.programs.blur, "direction", 0.0, 1.0/float32(bh))

p.ctx.DrawArrays(triangleStrip, 0, 4)
p.logError()
}

func (p *painter) drawCircle(circle *canvas.Circle, pos fyne.Position, frame fyne.Size) {
radius := paint.GetMaximumRadius(circle.Size())
program := p.roundRectangleProgram
program := p.programs.roundRectangle

// Vertex: BEG
bounds, points := p.vecSquareCoords(pos, circle, frame, circle.Shadow)
Expand Down Expand Up @@ -224,20 +224,20 @@ func (p *painter) drawLine(line *canvas.Line, pos fyne.Position, frame fyne.Size
return
}
points, halfWidth, feather := p.lineCoords(pos, line.Position1, line.Position2, line.StrokeWidth, 0.5, frame)
p.ctx.UseProgram(p.lineProgram.ref)
p.updateBuffer(p.lineProgram.buff, points)
p.UpdateVertexArray(p.lineProgram, "vert", 2, 4, 0)
p.UpdateVertexArray(p.lineProgram, "normal", 2, 4, 2)
p.ctx.UseProgram(p.programs.line.ref)
p.updateBuffer(p.programs.line.buff, points)
p.UpdateVertexArray(p.programs.line, "vert", 2, 4, 0)
p.UpdateVertexArray(p.programs.line, "normal", 2, 4, 2)

p.ctx.BlendFunc(srcAlpha, oneMinusSrcAlpha)
p.logError()

r, g, b, a := getFragmentColor(line.StrokeColor)
p.SetUniform4f(p.lineProgram, "color", r, g, b, a)
p.SetUniform4f(p.programs.line, "color", r, g, b, a)

p.SetUniform1f(p.lineProgram, "lineWidth", halfWidth)
p.SetUniform1f(p.programs.line, "lineWidth", halfWidth)

p.SetUniform1f(p.lineProgram, "feather", feather)
p.SetUniform1f(p.programs.line, "feather", feather)

p.ctx.DrawArrays(triangles, 0, 6)
p.logError()
Expand All @@ -250,7 +250,7 @@ func (p *painter) drawBezierCurve(bezierCurve *canvas.BezierCurve, pos fyne.Posi

// Vertex: BEG
bounds, points := p.vecRectCoords(pos, bezierCurve, frame, 0.0, canvas.Shadow{})
program := p.bezierCurveProgram
program := p.programs.bezierCurve
p.ctx.UseProgram(program.ref)
p.updateBuffer(program.buff, points)
p.UpdateVertexArray(program, "vert", 2, 4, 0)
Expand Down Expand Up @@ -315,7 +315,7 @@ func (p *painter) drawArbitraryPolygon(polygon *canvas.ArbitraryPolygon, pos fyn

// Vertex: BEG
bounds, points := p.vecRectCoords(pos, polygon, frame, 0.0, canvas.Shadow{})
program := p.arbitraryPolygonProgram
program := p.programs.arbitraryPolygon
p.ctx.UseProgram(program.ref)
p.updateBuffer(program.buff, points)
p.UpdateVertexArray(program, "vert", 2, 4, 0)
Expand Down Expand Up @@ -550,9 +550,9 @@ func (p *painter) drawOblong(obj fyne.CanvasObject, fill, stroke color.Color, st
roundedCorners := topRightRadius != 0 || topLeftRadius != 0 || bottomRightRadius != 0 || bottomLeftRadius != 0
var program programState
if roundedCorners {
program = p.roundRectangleProgram
program = p.programs.roundRectangle
} else {
program = p.rectangleProgram
program = p.programs.rectangle
}

// Vertex: BEG
Expand Down Expand Up @@ -644,7 +644,7 @@ func (p *painter) drawPolygon(polygon *canvas.RegularPolygon, pos fyne.Position,

// Vertex: BEG
bounds, points := p.vecRectCoords(pos, polygon, frame, 0.0, canvas.Shadow{})
program := p.polygonProgram
program := p.programs.polygon
p.ctx.UseProgram(program.ref)
p.updateBuffer(program.buff, points)
p.UpdateVertexArray(program, "vert", 2, 4, 0)
Expand Down Expand Up @@ -702,7 +702,7 @@ func (p *painter) drawArc(arc *canvas.Arc, pos fyne.Position, frame fyne.Size) {

// Vertex: BEG
bounds, points := p.vecRectCoords(pos, arc, frame, 0.0, canvas.Shadow{})
program := p.arcProgram
program := p.programs.arc
p.ctx.UseProgram(program.ref)
p.updateBuffer(program.buff, points)
p.UpdateVertexArray(program, "vert", 2, 4, 0)
Expand Down Expand Up @@ -762,7 +762,7 @@ func (p *painter) drawEllipse(ellipse *canvas.Ellipse, pos fyne.Position, frame
size := ellipse.Size()
radiusX := size.Width / 2
radiusY := size.Height / 2
program := p.ellipseProgram
program := p.programs.ellipse

// when rotated, the ellipse needs more space
// add half the difference between width and height as padding
Expand Down Expand Up @@ -914,15 +914,15 @@ func (p *painter) drawTextureRegion(texture Texture, pos fyne.Position, size, fr
points, insets := p.rectCoords(size, pos, frame, canvas.ImageFillStretch, 1, 0)
inner, _ := rectInnerCoords(size, pos, canvas.ImageFillStretch, 1)

p.ctx.UseProgram(p.program.ref)
p.updateBuffer(p.program.buff, points)
p.UpdateVertexArray(p.program, "vert", 3, 5, 0)
p.UpdateVertexArray(p.program, "vertTexCoord", 2, 5, 3)
p.ctx.UseProgram(p.programs.simple.ref)
p.updateBuffer(p.programs.simple.buff, points[:])
p.UpdateVertexArray(p.programs.simple, "vert", 3, 5, 0)
p.UpdateVertexArray(p.programs.simple, "vertTexCoord", 2, 5, 3)

p.SetUniform1f(p.program, "cornerRadius", 0)
p.SetUniform2f(p.program, "size", inner.Width*p.pixScale, inner.Height*p.pixScale)
p.SetUniform4f(p.program, "inset", insets[0], insets[1], insets[2], insets[3])
p.SetUniform1f(p.program, "alpha", 1.0)
p.SetUniform1f(p.programs.simple, "cornerRadius", 0)
p.SetUniform2f(p.programs.simple, "size", inner.Width*p.pixScale, inner.Height*p.pixScale)
p.SetUniform4f(p.programs.simple, "inset", insets[0], insets[1], insets[2], insets[3])
p.SetUniform1f(p.programs.simple, "alpha", 1.0)

p.ctx.BlendFunc(one, oneMinusSrcAlpha)
p.logError()
Expand Down Expand Up @@ -957,18 +957,18 @@ func (p *painter) drawTextureWithDetails(o fyne.CanvasObject, creator func(canva
points, insets := p.rectCoords(size, pos, frame, fill, aspect, pad)
inner, _ := rectInnerCoords(size, pos, fill, aspect)

p.ctx.UseProgram(p.program.ref)
p.updateBuffer(p.program.buff, points)
p.UpdateVertexArray(p.program, "vert", 3, 5, 0)
p.UpdateVertexArray(p.program, "vertTexCoord", 2, 5, 3)
p.ctx.UseProgram(p.programs.simple.ref)
p.updateBuffer(p.programs.simple.buff, points[:])
p.UpdateVertexArray(p.programs.simple, "vert", 3, 5, 0)
p.UpdateVertexArray(p.programs.simple, "vertTexCoord", 2, 5, 3)

// Set corner radius and texture size in pixels
cornerRadius = fyne.Min(paint.GetMaximumRadius(size), cornerRadius)
p.SetUniform1f(p.program, "cornerRadius", cornerRadius*p.pixScale)
p.SetUniform2f(p.program, "size", inner.Width*p.pixScale, inner.Height*p.pixScale)
p.SetUniform4f(p.program, "inset", insets[0], insets[1], insets[2], insets[3]) // texture coordinate insets (minX, minY, maxX, maxY)
p.SetUniform1f(p.programs.simple, "cornerRadius", cornerRadius*p.pixScale)
p.SetUniform2f(p.programs.simple, "size", inner.Width*p.pixScale, inner.Height*p.pixScale)
p.SetUniform4f(p.programs.simple, "inset", insets[0], insets[1], insets[2], insets[3]) // texture coordinate insets (minX, minY, maxX, maxY)

p.SetUniform1f(p.program, "alpha", alpha)
p.SetUniform1f(p.programs.simple, "alpha", alpha)

p.ctx.BlendFunc(one, oneMinusSrcAlpha)
p.logError()
Expand Down Expand Up @@ -1040,7 +1040,7 @@ func (p *painter) lineCoords(pos, pos1, pos2 fyne.Position, lineWidth, feather f
// rectCoords calculates the openGL coordinate space of a rectangle
func (p *painter) rectCoords(size fyne.Size, pos fyne.Position, frame fyne.Size,
fill canvas.ImageFill, aspect float32, pad float32,
) ([]float32, [4]float32) {
) ([20]float32, [4]float32) {
size, pos = rectInnerCoords(size, pos, fill, aspect)
size, pos = roundToPixelCoords(size, pos, p.pixScale)

Expand Down Expand Up @@ -1073,7 +1073,7 @@ func (p *painter) rectCoords(size fyne.Size, pos fyne.Position, frame fyne.Size,

insets := [4]float32{xInset, yInset, 1.0 - xInset, 1.0 - yInset}

return []float32{
return [20]float32{
// coord x, y, z texture x, y
x1, y2, 0, insets[0], insets[3], // top left
x1, y1, 0, insets[0], insets[1], // bottom left
Expand Down
20 changes: 10 additions & 10 deletions internal/painter/gl/gl_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,70 +80,70 @@ func (p *painter) Init() {
gl.Disable(gl.DEPTH_TEST)
gl.Enable(gl.BLEND)
p.logError()
p.program = programState{
p.programs.simple = programState{
ref: p.createProgram("simple"),
buff: p.createBuffer(20),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.blurProgram = programState{
p.programs.blur = programState{
ref: p.createProgram("blur"),
buff: p.createBuffer(20),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.lineProgram = programState{
p.programs.line = programState{
ref: p.createProgram("line"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.rectangleProgram = programState{
p.programs.rectangle = programState{
ref: p.createProgram("rectangle"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.roundRectangleProgram = programState{
p.programs.roundRectangle = programState{
ref: p.createProgram("round_rectangle"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.polygonProgram = programState{
p.programs.polygon = programState{
ref: p.createProgram("polygon"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.arcProgram = programState{
p.programs.arc = programState{
ref: p.createProgram("arc"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.bezierCurveProgram = programState{
p.programs.bezierCurve = programState{
ref: p.createProgram("bezier_curve"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.arbitraryPolygonProgram = programState{
p.programs.arbitraryPolygon = programState{
ref: p.createProgram("arbitrary_polygon"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
attributes: make(map[string]Attribute),
}

p.ellipseProgram = programState{
p.programs.ellipse = programState{
ref: p.createProgram("ellipse"),
buff: p.createBuffer(16),
uniforms: make(map[string]*uniformState),
Expand Down
Loading
Loading