draw.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package painter
  2. import (
  3. "image"
  4. "fyne.io/fyne/v2"
  5. "fyne.io/fyne/v2/canvas"
  6. "github.com/srwiley/rasterx"
  7. "golang.org/x/image/math/fixed"
  8. )
  9. // DrawCircle rasterizes the given circle object into an image.
  10. // The bounds of the output image will be increased by vectorPad to allow for stroke overflow at the edges.
  11. // The scale function is used to understand how many pixels are required per unit of size.
  12. func DrawCircle(circle *canvas.Circle, vectorPad float32, scale func(float32) float32) *image.RGBA {
  13. radius := fyne.Min(circle.Size().Width, circle.Size().Height) / 2
  14. width := int(scale(circle.Size().Width + vectorPad*2))
  15. height := int(scale(circle.Size().Height + vectorPad*2))
  16. stroke := scale(circle.StrokeWidth)
  17. raw := image.NewRGBA(image.Rect(0, 0, width, height))
  18. scanner := rasterx.NewScannerGV(int(circle.Size().Width), int(circle.Size().Height), raw, raw.Bounds())
  19. if circle.FillColor != nil {
  20. filler := rasterx.NewFiller(width, height, scanner)
  21. filler.SetColor(circle.FillColor)
  22. rasterx.AddCircle(float64(width/2), float64(height/2), float64(scale(radius)), filler)
  23. filler.Draw()
  24. }
  25. dasher := rasterx.NewDasher(width, height, scanner)
  26. dasher.SetColor(circle.StrokeColor)
  27. dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
  28. rasterx.AddCircle(float64(width/2), float64(height/2), float64(scale(radius)), dasher)
  29. dasher.Draw()
  30. return raw
  31. }
  32. // DrawLine rasterizes the given line object into an image.
  33. // The bounds of the output image will be increased by vectorPad to allow for stroke overflow at the edges.
  34. // The scale function is used to understand how many pixels are required per unit of size.
  35. func DrawLine(line *canvas.Line, vectorPad float32, scale func(float32) float32) *image.RGBA {
  36. col := line.StrokeColor
  37. width := int(scale(line.Size().Width + vectorPad*2))
  38. height := int(scale(line.Size().Height + vectorPad*2))
  39. stroke := scale(line.StrokeWidth)
  40. if stroke < 1 { // software painter doesn't fade lines to compensate
  41. stroke = 1
  42. }
  43. raw := image.NewRGBA(image.Rect(0, 0, width, height))
  44. scanner := rasterx.NewScannerGV(int(line.Size().Width), int(line.Size().Height), raw, raw.Bounds())
  45. dasher := rasterx.NewDasher(width, height, scanner)
  46. dasher.SetColor(col)
  47. dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
  48. p1x, p1y := scale(line.Position1.X-line.Position().X+vectorPad), scale(line.Position1.Y-line.Position().Y+vectorPad)
  49. p2x, p2y := scale(line.Position2.X-line.Position().X+vectorPad), scale(line.Position2.Y-line.Position().Y+vectorPad)
  50. if stroke <= 1.5 { // adjust to support 1px
  51. if p1x == p2x {
  52. p1x -= 0.5
  53. p2x -= 0.5
  54. }
  55. if p1y == p2y {
  56. p1y -= 0.5
  57. p2y -= 0.5
  58. }
  59. }
  60. dasher.Start(rasterx.ToFixedP(float64(p1x), float64(p1y)))
  61. dasher.Line(rasterx.ToFixedP(float64(p2x), float64(p2y)))
  62. dasher.Stop(true)
  63. dasher.Draw()
  64. return raw
  65. }
  66. // DrawRectangle rasterizes the given rectangle object with stroke border into an image.
  67. // The bounds of the output image will be increased by vectorPad to allow for stroke overflow at the edges.
  68. // The scale function is used to understand how many pixels are required per unit of size.
  69. func DrawRectangle(rect *canvas.Rectangle, vectorPad float32, scale func(float32) float32) *image.RGBA {
  70. width := int(scale(rect.Size().Width + vectorPad*2))
  71. height := int(scale(rect.Size().Height + vectorPad*2))
  72. stroke := scale(rect.StrokeWidth)
  73. raw := image.NewRGBA(image.Rect(0, 0, width, height))
  74. scanner := rasterx.NewScannerGV(int(rect.Size().Width), int(rect.Size().Height), raw, raw.Bounds())
  75. scaledPad := scale(vectorPad)
  76. p1x, p1y := scaledPad, scaledPad
  77. p2x, p2y := scale(rect.Size().Width)+scaledPad, scaledPad
  78. p3x, p3y := scale(rect.Size().Width)+scaledPad, scale(rect.Size().Height)+scaledPad
  79. p4x, p4y := scaledPad, scale(rect.Size().Height)+scaledPad
  80. if rect.FillColor != nil {
  81. filler := rasterx.NewFiller(width, height, scanner)
  82. filler.SetColor(rect.FillColor)
  83. rasterx.AddRect(float64(p1x), float64(p1y), float64(p3x), float64(p3y), 0, filler)
  84. filler.Draw()
  85. }
  86. if rect.StrokeColor != nil && rect.StrokeWidth > 0 {
  87. dasher := rasterx.NewDasher(width, height, scanner)
  88. dasher.SetColor(rect.StrokeColor)
  89. dasher.SetStroke(fixed.Int26_6(float64(stroke)*64), 0, nil, nil, nil, 0, nil, 0)
  90. dasher.Start(rasterx.ToFixedP(float64(p1x), float64(p1y)))
  91. dasher.Line(rasterx.ToFixedP(float64(p2x), float64(p2y)))
  92. dasher.Line(rasterx.ToFixedP(float64(p3x), float64(p3y)))
  93. dasher.Line(rasterx.ToFixedP(float64(p4x), float64(p4y)))
  94. dasher.Stop(true)
  95. dasher.Draw()
  96. }
  97. return raw
  98. }