util.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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, fillRule ebiten.FillRule) {
  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. op.FillRule = fillRule
  59. dst.DrawTriangles(vs, is, whiteSubImage, op)
  60. }
  61. // StrokeLine strokes a line (x0, y0)-(x1, y1) with the specified width and color.
  62. //
  63. // clr has be to be a solid (non-transparent) color.
  64. func StrokeLine(dst *ebiten.Image, x0, y0, x1, y1 float32, strokeWidth float32, clr color.Color, antialias bool) {
  65. var path Path
  66. path.MoveTo(x0, y0)
  67. path.LineTo(x1, y1)
  68. strokeOp := &StrokeOptions{}
  69. strokeOp.Width = strokeWidth
  70. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  71. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp)
  72. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRuleFillAll)
  73. return vs, is
  74. })
  75. }
  76. // DrawFilledRect fills a rectangle with the specified width and color.
  77. func DrawFilledRect(dst *ebiten.Image, x, y, width, height float32, clr color.Color, antialias bool) {
  78. var path Path
  79. path.MoveTo(x, y)
  80. path.LineTo(x, y+height)
  81. path.LineTo(x+width, y+height)
  82. path.LineTo(x+width, y)
  83. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  84. vs, is = path.AppendVerticesAndIndicesForFilling(vs, is)
  85. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRuleFillAll)
  86. return vs, is
  87. })
  88. }
  89. // StrokeRect strokes a rectangle with the specified width and color.
  90. //
  91. // clr has be to be a solid (non-transparent) color.
  92. func StrokeRect(dst *ebiten.Image, x, y, width, height float32, strokeWidth float32, clr color.Color, antialias bool) {
  93. var path Path
  94. path.MoveTo(x, y)
  95. path.LineTo(x, y+height)
  96. path.LineTo(x+width, y+height)
  97. path.LineTo(x+width, y)
  98. path.Close()
  99. strokeOp := &StrokeOptions{}
  100. strokeOp.Width = strokeWidth
  101. strokeOp.MiterLimit = 10
  102. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  103. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp)
  104. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRuleFillAll)
  105. return vs, is
  106. })
  107. }
  108. // DrawFilledCircle fills a circle with the specified center position (cx, cy), the radius (r), width and color.
  109. func DrawFilledCircle(dst *ebiten.Image, cx, cy, r float32, clr color.Color, antialias bool) {
  110. var path Path
  111. path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
  112. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  113. vs, is = path.AppendVerticesAndIndicesForFilling(vs, is)
  114. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRuleFillAll)
  115. return vs, is
  116. })
  117. }
  118. // StrokeCircle strokes a circle with the specified center position (cx, cy), the radius (r), width and color.
  119. //
  120. // clr has be to be a solid (non-transparent) color.
  121. func StrokeCircle(dst *ebiten.Image, cx, cy, r float32, strokeWidth float32, clr color.Color, antialias bool) {
  122. var path Path
  123. path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
  124. path.Close()
  125. strokeOp := &StrokeOptions{}
  126. strokeOp.Width = strokeWidth
  127. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  128. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp)
  129. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRuleFillAll)
  130. return vs, is
  131. })
  132. }
  133. // FillRule is the rule whether an overlapped region is rendered or not.
  134. type FillRule int
  135. const (
  136. // FillRuleNonZero means that triangles are rendered based on the non-zero rule.
  137. // If and only if the number of overlaps is not 0, the region is rendered.
  138. FillRuleNonZero FillRule = FillRule(ebiten.FillRuleNonZero)
  139. // FillRuleEvenOdd means that triangles are rendered based on the even-odd rule.
  140. // If and only if the number of overlaps is odd, the region is rendered.
  141. FillRuleEvenOdd FillRule = FillRule(ebiten.FillRuleEvenOdd)
  142. )
  143. // DrawFilledRect fills the specified path with the specified color.
  144. func DrawFilledPath(dst *ebiten.Image, path *Path, clr color.Color, antialias bool, fillRule FillRule) {
  145. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  146. vs, is = path.AppendVerticesAndIndicesForFilling(vs, is)
  147. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRule(fillRule))
  148. return vs, is
  149. })
  150. }
  151. // StrokeCircle strokes the specified path with the specified color and stroke options.
  152. //
  153. // clr has be to be a solid (non-transparent) color.
  154. func StrokePath(dst *ebiten.Image, path *Path, clr color.Color, antialias bool, options *StrokeOptions) {
  155. useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) {
  156. vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, options)
  157. drawVerticesForUtil(dst, vs, is, clr, antialias, ebiten.FillRuleFillAll)
  158. return vs, is
  159. })
  160. }