util.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright 2022 The Ebitengine Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package vector
  15. import (
  16. "image"
  17. "image/color"
  18. "math"
  19. "sync"
  20. "github.com/hajimehoshi/ebiten/v2"
  21. )
  22. var (
  23. whiteImage = ebiten.NewImage(3, 3)
  24. whiteSubImage = whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
  25. )
  26. var (
  27. cachedVertices []ebiten.Vertex
  28. cachedIndices []uint16
  29. cacheM sync.Mutex
  30. )
  31. func useCachedVerticesAndIndices(fn func([]ebiten.Vertex, []uint16) (vs []ebiten.Vertex, is []uint16)) {
  32. cacheM.Lock()
  33. defer cacheM.Unlock()
  34. cachedVertices, cachedIndices = fn(cachedVertices[:0], cachedIndices[:0])
  35. }
  36. func init() {
  37. b := whiteImage.Bounds()
  38. pix := make([]byte, 4*b.Dx()*b.Dy())
  39. for i := range pix {
  40. pix[i] = 0xff
  41. }
  42. // This is hacky, but WritePixels is better than Fill in term of automatic texture packing.
  43. whiteImage.WritePixels(pix)
  44. }
  45. func drawVerticesForUtil(dst *ebiten.Image, vs []ebiten.Vertex, is []uint16, clr color.Color, antialias bool) {
  46. r, g, b, a := clr.RGBA()
  47. for i := range vs {
  48. vs[i].SrcX = 1
  49. vs[i].SrcY = 1
  50. vs[i].ColorR = float32(r) / 0xffff
  51. vs[i].ColorG = float32(g) / 0xffff
  52. vs[i].ColorB = float32(b) / 0xffff
  53. vs[i].ColorA = float32(a) / 0xffff
  54. }
  55. op := &ebiten.DrawTrianglesOptions{}
  56. op.ColorScaleMode = ebiten.ColorScaleModePremultipliedAlpha
  57. op.AntiAlias = antialias
  58. dst.DrawTriangles(vs, is, whiteSubImage, op)
  59. }
  60. // StrokeLine strokes a line (x0, y0)-(x1, y1) with the specified width and color.
  61. //
  62. // clr has be to be a solid (non-transparent) color.
  63. func StrokeLine(dst *ebiten.Image, x0, y0, x1, y1 float32, strokeWidth float32, clr color.Color, antialias bool) {
  64. var path Path
  65. path.MoveTo(x0, y0)
  66. path.LineTo(x1, y1)
  67. strokeOp := &StrokeOptions{}
  68. strokeOp.Width = strokeWidth
  69. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  70. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp)
  71. drawVerticesForUtil(dst, vs, is, clr, antialias)
  72. return vs, is
  73. })
  74. }
  75. // DrawFilledRect fills a rectangle with the specified width and color.
  76. func DrawFilledRect(dst *ebiten.Image, x, y, width, height float32, clr color.Color, antialias bool) {
  77. var path Path
  78. path.MoveTo(x, y)
  79. path.LineTo(x, y+height)
  80. path.LineTo(x+width, y+height)
  81. path.LineTo(x+width, y)
  82. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  83. vs, is = path.AppendVerticesAndIndicesForFilling(vs, is)
  84. drawVerticesForUtil(dst, vs, is, clr, antialias)
  85. return vs, is
  86. })
  87. }
  88. // StrokeRect strokes a rectangle with the specified width and color.
  89. //
  90. // clr has be to be a solid (non-transparent) color.
  91. func StrokeRect(dst *ebiten.Image, x, y, width, height float32, strokeWidth float32, clr color.Color, antialias bool) {
  92. var path Path
  93. path.MoveTo(x, y)
  94. path.LineTo(x, y+height)
  95. path.LineTo(x+width, y+height)
  96. path.LineTo(x+width, y)
  97. path.Close()
  98. strokeOp := &StrokeOptions{}
  99. strokeOp.Width = strokeWidth
  100. strokeOp.MiterLimit = 10
  101. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  102. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp)
  103. drawVerticesForUtil(dst, vs, is, clr, antialias)
  104. return vs, is
  105. })
  106. }
  107. // DrawFilledCircle fills a circle with the specified center position (cx, cy), the radius (r), width and color.
  108. func DrawFilledCircle(dst *ebiten.Image, cx, cy, r float32, clr color.Color, antialias bool) {
  109. var path Path
  110. path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
  111. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  112. vs, is = path.AppendVerticesAndIndicesForFilling(vs, is)
  113. drawVerticesForUtil(dst, vs, is, clr, antialias)
  114. return vs, is
  115. })
  116. }
  117. // StrokeCircle strokes a circle with the specified center position (cx, cy), the radius (r), width and color.
  118. //
  119. // clr has be to be a solid (non-transparent) color.
  120. func StrokeCircle(dst *ebiten.Image, cx, cy, r float32, strokeWidth float32, clr color.Color, antialias bool) {
  121. var path Path
  122. path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
  123. path.Close()
  124. strokeOp := &StrokeOptions{}
  125. strokeOp.Width = strokeWidth
  126. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  127. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp)
  128. drawVerticesForUtil(dst, vs, is, clr, antialias)
  129. return vs, is
  130. })
  131. }