| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- package painter
- import (
- "image"
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/canvas"
- "github.com/srwiley/rasterx"
- "golang.org/x/image/math/fixed"
- )
- const quarterCircleControl = 1 - 0.55228
- // 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 float32, scale func(float32) float32) *image.RGBA {
- radius := fyne.Min(circle.Size().Width, circle.Size().Height) / 2
- width := int(scale(circle.Size().Width + vectorPad*2))
- height := int(scale(circle.Size().Height + vectorPad*2))
- stroke := scale(circle.StrokeWidth)
- raw := image.NewRGBA(image.Rect(0, 0, width, height))
- scanner := rasterx.NewScannerGV(int(circle.Size().Width), int(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
- }
- // DrawLine rasterizes the given line 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 DrawLine(line *canvas.Line, vectorPad float32, scale func(float32) float32) *image.RGBA {
- col := line.StrokeColor
- size := line.Size()
- width := int(scale(size.Width + vectorPad*2))
- height := int(scale(size.Height + vectorPad*2))
- stroke := scale(line.StrokeWidth)
- if stroke < 1 { // software painter doesn't fade lines to compensate
- stroke = 1
- }
- raw := image.NewRGBA(image.Rect(0, 0, width, height))
- scanner := rasterx.NewScannerGV(int(size.Width), int(size.Height), raw, raw.Bounds())
- dasher := rasterx.NewDasher(width, height, scanner)
- dasher.SetColor(col)
- dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
- positon := line.Position()
- p1x, p1y := scale(line.Position1.X-positon.X+vectorPad), scale(line.Position1.Y-positon.Y+vectorPad)
- p2x, p2y := scale(line.Position2.X-positon.X+vectorPad), scale(line.Position2.Y-positon.Y+vectorPad)
- if stroke <= 1.5 { // adjust to support 1px
- if p1x == p2x {
- p1x -= 0.5
- p2x -= 0.5
- }
- if p1y == p2y {
- p1y -= 0.5
- p2y -= 0.5
- }
- }
- dasher.Start(rasterx.ToFixedP(float64(p1x), float64(p1y)))
- dasher.Line(rasterx.ToFixedP(float64(p2x), float64(p2y)))
- dasher.Stop(true)
- dasher.Draw()
- return raw
- }
- // DrawRectangle rasterizes the given rectangle object with stroke border 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 DrawRectangle(rect *canvas.Rectangle, vectorPad float32, scale func(float32) float32) *image.RGBA {
- size := rect.Size()
- width := int(scale(size.Width + vectorPad*2))
- height := int(scale(size.Height + vectorPad*2))
- stroke := scale(rect.StrokeWidth)
- raw := image.NewRGBA(image.Rect(0, 0, width, height))
- scanner := rasterx.NewScannerGV(int(size.Width), int(size.Height), raw, raw.Bounds())
- scaledPad := scale(vectorPad)
- p1x, p1y := scaledPad, scaledPad
- p2x, p2y := scale(size.Width)+scaledPad, scaledPad
- p3x, p3y := scale(size.Width)+scaledPad, scale(size.Height)+scaledPad
- p4x, p4y := scaledPad, scale(rect.Size().Height)+scaledPad
- if rect.FillColor != nil {
- filler := rasterx.NewFiller(width, height, scanner)
- filler.SetColor(rect.FillColor)
- if rect.CornerRadius == 0 {
- rasterx.AddRect(float64(p1x), float64(p1y), float64(p3x), float64(p3y), 0, filler)
- } else {
- r := float64(scale(rect.CornerRadius))
- rasterx.AddRoundRect(float64(p1x), float64(p1y), float64(p3x), float64(p3y), r, r, 0, rasterx.RoundGap, filler)
- }
- filler.Draw()
- }
- if rect.StrokeColor != nil && rect.StrokeWidth > 0 {
- r := scale(rect.CornerRadius)
- c := quarterCircleControl * r
- dasher := rasterx.NewDasher(width, height, scanner)
- dasher.SetColor(rect.StrokeColor)
- dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
- if c != 0 {
- dasher.Start(rasterx.ToFixedP(float64(p1x), float64(p1y+r)))
- dasher.CubeBezier(rasterx.ToFixedP(float64(p1x), float64(p1y+c)), rasterx.ToFixedP(float64(p1x+c), float64(p1y)), rasterx.ToFixedP(float64(p1x+r), float64(p2y)))
- } else {
- dasher.Start(rasterx.ToFixedP(float64(p1x), float64(p1y)))
- }
- dasher.Line(rasterx.ToFixedP(float64(p2x-r), float64(p2y)))
- if c != 0 {
- dasher.CubeBezier(rasterx.ToFixedP(float64(p2x-c), float64(p2y)), rasterx.ToFixedP(float64(p2x), float64(p2y+c)), rasterx.ToFixedP(float64(p2x), float64(p2y+r)))
- }
- dasher.Line(rasterx.ToFixedP(float64(p3x), float64(p3y-r)))
- if c != 0 {
- dasher.CubeBezier(rasterx.ToFixedP(float64(p3x), float64(p3y-c)), rasterx.ToFixedP(float64(p3x-c), float64(p3y)), rasterx.ToFixedP(float64(p3x-r), float64(p3y)))
- }
- dasher.Line(rasterx.ToFixedP(float64(p4x+r), float64(p4y)))
- if c != 0 {
- dasher.CubeBezier(rasterx.ToFixedP(float64(p4x+c), float64(p4y)), rasterx.ToFixedP(float64(p4x), float64(p4y-c)), rasterx.ToFixedP(float64(p4x), float64(p4y-r)))
- }
- dasher.Stop(true)
- dasher.Draw()
- }
- return raw
- }
|