fill.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright 2018 by the rasterx Authors. All rights reserved.
  2. //_
  3. // Created 2017 by S.R.Wiley
  4. package rasterx
  5. import (
  6. "image"
  7. "image/color"
  8. "math"
  9. "golang.org/x/image/math/fixed"
  10. )
  11. type (
  12. // ColorFunc maps a color to x y coordinates
  13. ColorFunc func(x, y int) color.Color
  14. // Scanner interface for path generating types
  15. Scanner interface {
  16. Start(a fixed.Point26_6)
  17. Line(b fixed.Point26_6)
  18. Draw()
  19. GetPathExtent() fixed.Rectangle26_6
  20. SetBounds(w, h int)
  21. SetColor(color interface{})
  22. SetWinding(useNonZeroWinding bool)
  23. Clear()
  24. // SetClip sets an optional clipping rectangle to restrict rendering
  25. // only to that region -- if size is 0 then ignored (set to image.ZR
  26. // to clear)
  27. SetClip(rect image.Rectangle)
  28. }
  29. // Adder interface for types that can accumlate path commands
  30. Adder interface {
  31. // Start starts a new curve at the given point.
  32. Start(a fixed.Point26_6)
  33. // Line adds a line segment to the path
  34. Line(b fixed.Point26_6)
  35. // QuadBezier adds a quadratic bezier curve to the path
  36. QuadBezier(b, c fixed.Point26_6)
  37. // CubeBezier adds a cubic bezier curve to the path
  38. CubeBezier(b, c, d fixed.Point26_6)
  39. // Closes the path to the start point if closeLoop is true
  40. Stop(closeLoop bool)
  41. }
  42. // Rasterx extends the adder interface to include lineF and joinF functions
  43. Rasterx interface {
  44. Adder
  45. lineF(b fixed.Point26_6)
  46. joinF()
  47. }
  48. // Filler satisfies Rasterx
  49. Filler struct {
  50. Scanner
  51. a, first fixed.Point26_6
  52. }
  53. )
  54. // Start starts a new path at the given point.
  55. func (r *Filler) Start(a fixed.Point26_6) {
  56. r.a = a
  57. r.first = a
  58. r.Scanner.Start(a)
  59. }
  60. // Stop sends a path at the given point.
  61. func (r *Filler) Stop(isClosed bool) {
  62. if r.first != r.a {
  63. r.Line(r.first)
  64. }
  65. }
  66. // QuadBezier adds a quadratic segment to the current curve.
  67. func (r *Filler) QuadBezier(b, c fixed.Point26_6) {
  68. r.QuadBezierF(r, b, c)
  69. }
  70. // QuadTo flattens the quadratic Bezier curve into lines through the LineTo func
  71. // This functions is adapted from the version found in
  72. // golang.org/x/image/vector
  73. func QuadTo(ax, ay, bx, by, cx, cy float32, LineTo func(dx, dy float32)) {
  74. devsq := devSquared(ax, ay, bx, by, cx, cy)
  75. if devsq >= 0.333 {
  76. const tol = 3
  77. n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
  78. t, nInv := float32(0), 1/float32(n)
  79. for i := 0; i < n-1; i++ {
  80. t += nInv
  81. mt := 1 - t
  82. t1 := mt * mt
  83. t2 := mt * t * 2
  84. t3 := t * t
  85. LineTo(
  86. ax*t1+bx*t2+cx*t3,
  87. ay*t1+by*t2+cy*t3)
  88. }
  89. }
  90. LineTo(cx, cy)
  91. }
  92. // CubeTo flattens the cubic Bezier curve into lines through the LineTo func
  93. // This functions is adapted from the version found in
  94. // golang.org/x/image/vector
  95. func CubeTo(ax, ay, bx, by, cx, cy, dx, dy float32, LineTo func(ex, ey float32)) {
  96. devsq := devSquared(ax, ay, bx, by, dx, dy)
  97. if devsqAlt := devSquared(ax, ay, cx, cy, dx, dy); devsq < devsqAlt {
  98. devsq = devsqAlt
  99. }
  100. if devsq >= 0.333 {
  101. const tol = 3
  102. n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
  103. t, nInv := float32(0), 1/float32(n)
  104. for i := 0; i < n-1; i++ {
  105. t += nInv
  106. tsq := t * t
  107. mt := 1 - t
  108. mtsq := mt * mt
  109. t1 := mtsq * mt
  110. t2 := mtsq * t * 3
  111. t3 := mt * tsq * 3
  112. t4 := tsq * t
  113. LineTo(
  114. ax*t1+bx*t2+cx*t3+dx*t4,
  115. ay*t1+by*t2+cy*t3+dy*t4)
  116. }
  117. }
  118. LineTo(dx, dy)
  119. }
  120. // devSquared returns a measure of how curvy the sequence (ax, ay) to (bx, by)
  121. // to (cx, cy) is. It determines how many line segments will approximate a
  122. // Bézier curve segment. This functions is copied from the version found in
  123. // golang.org/x/image/vector as are the below comments.
  124. //
  125. // http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html
  126. // gives the rationale for this evenly spaced heuristic instead of a recursive
  127. // de Casteljau approach:
  128. //
  129. // The reason for the subdivision by n is that I expect the "flatness"
  130. // computation to be semi-expensive (it's done once rather than on each
  131. // potential subdivision) and also because you'll often get fewer subdivisions.
  132. // Taking a circular arc as a simplifying assumption (ie a spherical cow),
  133. // where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't
  134. // made any horrible mistakes, is expected to be 33% more in the limit.
  135. func devSquared(ax, ay, bx, by, cx, cy float32) float32 {
  136. devx := ax - 2*bx + cx
  137. devy := ay - 2*by + cy
  138. return devx*devx + devy*devy
  139. }
  140. // QuadBezierF adds a quadratic segment to the sgm Rasterizer.
  141. func (r *Filler) QuadBezierF(sgm Rasterx, b, c fixed.Point26_6) {
  142. // check for degenerate bezier
  143. if r.a == b || b == c {
  144. sgm.Line(c)
  145. return
  146. }
  147. sgm.joinF()
  148. QuadTo(float32(r.a.X), float32(r.a.Y), // Pts are x64, but does not matter.
  149. float32(b.X), float32(b.Y),
  150. float32(c.X), float32(c.Y),
  151. func(dx, dy float32) {
  152. sgm.lineF(fixed.Point26_6{X: fixed.Int26_6(dx), Y: fixed.Int26_6(dy)})
  153. })
  154. }
  155. // CubeBezier adds a cubic bezier to the curve
  156. func (r *Filler) CubeBezier(b, c, d fixed.Point26_6) {
  157. r.CubeBezierF(r, b, c, d)
  158. }
  159. // joinF is a no-op for a filling rasterizer. This is used in stroking and dashed
  160. // stroking
  161. func (r *Filler) joinF() {
  162. }
  163. // Line for a filling rasterizer is just the line call in scan
  164. func (r *Filler) Line(b fixed.Point26_6) {
  165. r.lineF(b)
  166. }
  167. // lineF for a filling rasterizer is just the line call in scan
  168. func (r *Filler) lineF(b fixed.Point26_6) {
  169. r.Scanner.Line(b)
  170. r.a = b
  171. }
  172. // CubeBezierF adds a cubic bezier to the curve. sending the line calls the the
  173. // sgm Rasterizer
  174. func (r *Filler) CubeBezierF(sgm Rasterx, b, c, d fixed.Point26_6) {
  175. if (r.a == b && c == d) || (r.a == b && b == c) || (c == b && d == c) {
  176. sgm.Line(d)
  177. return
  178. }
  179. sgm.joinF()
  180. CubeTo(float32(r.a.X), float32(r.a.Y),
  181. float32(b.X), float32(b.Y),
  182. float32(c.X), float32(c.Y),
  183. float32(d.X), float32(d.Y),
  184. func(ex, ey float32) {
  185. sgm.lineF(fixed.Point26_6{X: fixed.Int26_6(ex), Y: fixed.Int26_6(ey)})
  186. })
  187. }
  188. // Clear resets the filler
  189. func (r *Filler) Clear() {
  190. r.a = fixed.Point26_6{}
  191. r.first = r.a
  192. r.Scanner.Clear()
  193. }
  194. // SetBounds sets the maximum width and height of the rasterized image and
  195. // calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
  196. func (r *Filler) SetBounds(width, height int) {
  197. if width < 0 {
  198. width = 0
  199. }
  200. if height < 0 {
  201. height = 0
  202. }
  203. r.Scanner.SetBounds(width, height)
  204. r.Clear()
  205. }
  206. // NewFiller returns a Filler ptr with default values.
  207. // A Filler in addition to rasterizing lines like a Scann,
  208. // can also rasterize quadratic and cubic bezier curves.
  209. // If Scanner is nil default scanner ScannerGV is used
  210. func NewFiller(width, height int, scanner Scanner) *Filler {
  211. r := new(Filler)
  212. r.Scanner = scanner
  213. r.SetBounds(width, height)
  214. r.SetWinding(true)
  215. return r
  216. }