Skip to content

Commit

Permalink
Merge pull request fyne-io#1130 from andydotxyz/software_circle
Browse files Browse the repository at this point in the history
Add a simple circle renderer to software painter
  • Loading branch information
andydotxyz committed Jun 29, 2020
2 parents 51c5d78 + b0d78af commit 77532fa
Show file tree
Hide file tree
Showing 39 changed files with 77 additions and 20 deletions.
40 changes: 40 additions & 0 deletions internal/painter/draw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package painter

import (
"image"
"math"

"fyne.io/fyne/canvas"

"github.com/srwiley/rasterx"
"golang.org/x/image/math/fixed"
)

// DrawCircle rasterizes the given circle object into an image.
// The bounds of the output image will be increased by vectorPad to allow for stroke overflow at the edges.
// The scale function is used to understand how many pixels are required per unit of size.
func DrawCircle(circle *canvas.Circle, vectorPad int, scale func(float32) int) *image.RGBA {
radius := float32(math.Min(float64(circle.Size().Width), float64(circle.Size().Height)) / 2)

width := scale(float32(circle.Size().Width + vectorPad*2))
height := scale(float32(circle.Size().Height + vectorPad*2))
stroke := scale(circle.StrokeWidth)

raw := image.NewRGBA(image.Rect(0, 0, width, height))
scanner := rasterx.NewScannerGV(circle.Size().Width, circle.Size().Height, raw, raw.Bounds())

if circle.FillColor != nil {
filler := rasterx.NewFiller(width, height, scanner)
filler.SetColor(circle.FillColor)
rasterx.AddCircle(float64(width/2), float64(height/2), float64(scale(radius)), filler)
filler.Draw()
}

dasher := rasterx.NewDasher(width, height, scanner)
dasher.SetColor(circle.StrokeColor)
dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
rasterx.AddCircle(float64(width/2), float64(height/2), float64(scale(radius)), dasher)
dasher.Draw()

return raw
}
21 changes: 1 addition & 20 deletions internal/painter/gl/gl_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,8 @@ func getTexture(object fyne.CanvasObject, creator func(canvasObject fyne.CanvasO

func (p *glPainter) newGlCircleTexture(obj fyne.CanvasObject) Texture {
circle := obj.(*canvas.Circle)
radius := fyne.Min(circle.Size().Width, circle.Size().Height) / 2

width := p.textureScaleInt(circle.Size().Width + vectorPad*2)
height := p.textureScaleInt(circle.Size().Height + vectorPad*2)
stroke := circle.StrokeWidth * p.canvas.Scale() * p.texScale

raw := image.NewRGBA(image.Rect(0, 0, width, height))
scanner := rasterx.NewScannerGV(circle.Size().Width, circle.Size().Height, raw, raw.Bounds())

if circle.FillColor != nil {
filler := rasterx.NewFiller(width, height, scanner)
filler.SetColor(circle.FillColor)
rasterx.AddCircle(float64(width/2), float64(height/2), float64(p.textureScaleInt(radius)), filler)
filler.Draw()
}

dasher := rasterx.NewDasher(width, height, scanner)
dasher.SetColor(circle.StrokeColor)
dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
rasterx.AddCircle(float64(width/2), float64(height/2), float64(p.textureScaleInt(radius)), dasher)
dasher.Draw()
raw := painter.DrawCircle(circle, vectorPad, p.textureScale)

return p.imgToTexture(raw, canvas.ImageScaleSmooth)
}
Expand Down
7 changes: 7 additions & 0 deletions internal/painter/gl/painter.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ func (p *glPainter) textureScaleInt(v int) int {
return int(math.Round(float64(v) * float64(p.canvas.Scale()*p.texScale)))
}

func (p *glPainter) textureScale(v float32) int {
if p.canvas.Scale() == 1.0 && p.texScale == 1.0 {
return int(v)
}
return int(math.Round(float64(v) * float64(p.canvas.Scale()*p.texScale)))
}

var startCacheMonitor = &sync.Once{}

// NewPainter creates a new GL based renderer for the provided canvas.
Expand Down
14 changes: 14 additions & 0 deletions internal/painter/software/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package software
import (
"fmt"
"image"
"math"

"fyne.io/fyne"
"fyne.io/fyne/canvas"
Expand All @@ -21,6 +22,19 @@ type gradient interface {
Size() fyne.Size
}

func drawCircle(c fyne.Canvas, circle *canvas.Circle, pos fyne.Position, base *image.NRGBA, clip image.Rectangle) {
scaledWidth := internal.ScaleInt(c, circle.Size().Width)
scaledHeight := internal.ScaleInt(c, circle.Size().Height)
scaledX, scaledY := internal.ScaleInt(c, pos.X), internal.ScaleInt(c, pos.Y)
bounds := clip.Intersect(image.Rect(scaledX, scaledY, scaledX+scaledWidth, scaledY+scaledHeight))

raw := painter.DrawCircle(circle, 0, func(in float32) int {
return int(math.Round(float64(in) * float64(c.Scale())))
})

draw.Draw(base, bounds, raw, image.ZP, draw.Over)
}

func drawGradient(c fyne.Canvas, g gradient, pos fyne.Position, base *image.NRGBA, clip image.Rectangle) {
bounds := g.Size()
width := internal.ScaleInt(c, bounds.Width)
Expand Down
2 changes: 2 additions & 0 deletions internal/painter/software/painter.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func (*Painter) Paint(c fyne.Canvas) image.Image {
drawText(c, o, pos, base, clip)
case gradient:
drawGradient(c, o, pos, base, clip)
case *canvas.Circle:
drawCircle(c, o, pos, base, clip)
case *canvas.Rectangle:
drawRectangle(c, o, pos, base, clip)
case fyne.Widget:
Expand Down
13 changes: 13 additions & 0 deletions internal/painter/software/painter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ func makeTestImage(w, h int) image.Image {
return internalTest.NewCheckedImage(w, h, w, h)
}

func TestPainter_paintCircle(t *testing.T) {
test.ApplyTheme(t, theme.LightTheme())
obj := canvas.NewCircle(color.Black)

c := test.NewCanvas()
c.SetPadded(true)
c.SetContent(obj)
c.Resize(fyne.NewSize(70+2*theme.Padding(), 70+2*theme.Padding()))
p := software.NewPainter()

test.AssertImageMatches(t, "draw_circle.png", p.Paint(c))
}

func TestPainter_paintGradient_clipped(t *testing.T) {
test.ApplyTheme(t, theme.LightTheme())
g := canvas.NewRadialGradient(color.NRGBA{R: 200, A: 255}, color.NRGBA{B: 200, A: 255})
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/check/layout_checked.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/check/layout_checked_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/check/layout_unchecked.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/check/layout_unchecked_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple_horizontal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple_horizontal_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple_selected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple_selected_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_multiple_selected_horizontal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single_horizontal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single_horizontal_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single_selected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single_selected_disabled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/radio/layout_single_selected_horizontal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/tabcontainer/desktop/layout_bottom_icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/tabcontainer/desktop/layout_bottom_text.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/tabcontainer/desktop/layout_leading_icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified widget/testdata/tabcontainer/desktop/layout_leading_text.png
Binary file modified widget/testdata/tabcontainer/desktop/layout_top_icon.png
Binary file modified widget/testdata/tabcontainer/desktop/layout_top_icon_and_text.png
Binary file modified widget/testdata/tabcontainer/desktop/layout_top_text.png
Binary file modified widget/testdata/tabcontainer/desktop/layout_trailing_icon.png
Binary file modified widget/testdata/tabcontainer/desktop/layout_trailing_text.png

0 comments on commit 77532fa

Please sign in to comment.