scan.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Package rasterx implements a rasterizer in go.
  2. // By default rasterx uses ScannerGV to render images
  3. // which uses the rasterizer in the golang.org/x/image/vector package.
  4. // The freetype rasterizer under the GNU license can also be used, by
  5. // downloading the scanFT package.
  6. //
  7. // Copyright 2018 All rights reserved.
  8. // Created: 5/12/2018 by S.R.Wiley
  9. package rasterx
  10. import (
  11. "image"
  12. "math"
  13. "image/color"
  14. "image/draw"
  15. "golang.org/x/image/math/fixed"
  16. "golang.org/x/image/vector"
  17. )
  18. // At returns the color at the point x,y
  19. func (c *ColorFuncImage) At(x, y int) color.Color {
  20. return c.colorFunc(x, y)
  21. }
  22. type (
  23. // ColorFuncImage implements and image
  24. // using the provided colorFunc
  25. ColorFuncImage struct {
  26. image.Uniform
  27. colorFunc ColorFunc
  28. }
  29. // ScannerGV uses the google vector rasterizer
  30. ScannerGV struct {
  31. r vector.Rasterizer
  32. //a, first fixed.Point26_6
  33. Dest draw.Image
  34. Targ image.Rectangle
  35. clipImage *ClipImage
  36. Source image.Image
  37. Offset image.Point
  38. minX, minY, maxX, maxY fixed.Int26_6 // keep track of bounds
  39. }
  40. )
  41. // ClipImage is a clipable ColorFuncImage
  42. type ClipImage struct {
  43. ColorFuncImage
  44. clip image.Rectangle
  45. }
  46. var noApha = color.RGBA{0, 0, 0, 0}
  47. // GetPathExtent returns the extent of the path
  48. func (s *ScannerGV) GetPathExtent() fixed.Rectangle26_6 {
  49. return fixed.Rectangle26_6{Min: fixed.Point26_6{X: s.minX, Y: s.minY}, Max: fixed.Point26_6{X: s.maxX, Y: s.maxY}}
  50. }
  51. // At returns the color of the ClipImage at the point x,y
  52. func (c *ClipImage) At(x, y int) color.Color {
  53. p := image.Point{x, y}
  54. if p.In(c.clip) {
  55. return c.ColorFuncImage.At(x, y)
  56. }
  57. return noApha
  58. }
  59. // SetWinding set the winding rule for the scanner
  60. func (s *ScannerGV) SetWinding(useNonZeroWinding bool) {
  61. // no-op as scanner gv does not support even-odd winding
  62. }
  63. // SetColor set the color type for the scanner
  64. func (s *ScannerGV) SetColor(clr interface{}) {
  65. switch c := clr.(type) {
  66. case color.Color:
  67. s.clipImage.ColorFuncImage.Uniform.C = c
  68. if s.clipImage.clip == image.ZR {
  69. s.Source = &s.clipImage.ColorFuncImage.Uniform
  70. } else {
  71. s.clipImage.ColorFuncImage.colorFunc = func(x, y int) color.Color {
  72. return c
  73. }
  74. s.Source = s.clipImage
  75. }
  76. case ColorFunc:
  77. s.clipImage.ColorFuncImage.colorFunc = c
  78. if s.clipImage.clip == image.ZR {
  79. s.Source = &s.clipImage.ColorFuncImage
  80. } else {
  81. s.Source = s.clipImage
  82. }
  83. }
  84. }
  85. // SetClip sets an optional clipping rectangle to restrict rendering only to
  86. // that region -- if size is 0 then ignored (set to image.ZR to clear)
  87. func (s *ScannerGV) SetClip(rect image.Rectangle) {
  88. s.clipImage.clip = rect
  89. if s.Source == &s.clipImage.ColorFuncImage.Uniform {
  90. s.SetColor(s.clipImage.ColorFuncImage.Uniform.C)
  91. } else {
  92. s.SetColor(s.clipImage.ColorFuncImage.colorFunc)
  93. }
  94. }
  95. func (s *ScannerGV) set(a fixed.Point26_6) {
  96. if s.maxX < a.X {
  97. s.maxX = a.X
  98. }
  99. if s.maxY < a.Y {
  100. s.maxY = a.Y
  101. }
  102. if s.minX > a.X {
  103. s.minX = a.X
  104. }
  105. if s.minY > a.Y {
  106. s.minY = a.Y
  107. }
  108. }
  109. // Start starts a new path at the given point.
  110. func (s *ScannerGV) Start(a fixed.Point26_6) {
  111. s.set(a)
  112. s.r.MoveTo(float32(a.X)/64, float32(a.Y)/64)
  113. }
  114. // Line adds a linear segment to the current curve.
  115. func (s *ScannerGV) Line(b fixed.Point26_6) {
  116. s.set(b)
  117. s.r.LineTo(float32(b.X)/64, float32(b.Y)/64)
  118. }
  119. // Draw renders the accumulate scan to the desination
  120. func (s *ScannerGV) Draw() {
  121. // This draws the entire bounds of the image, because
  122. // at this point the alpha mask does not shift with the
  123. // placement of the target rectangle in the vector rasterizer
  124. s.r.Draw(s.Dest, s.Dest.Bounds(), s.Source, s.Offset)
  125. // Remove the line above and uncomment the lines below if you
  126. // are using a version of the vector rasterizer that shifts the alpha
  127. // mask with the placement of the target
  128. // s.Targ.Min.X = int(s.minX >> 6)
  129. // s.Targ.Min.Y = int(s.minY >> 6)
  130. // s.Targ.Max.X = int(s.maxX>>6) + 1
  131. // s.Targ.Max.Y = int(s.maxY>>6) + 1
  132. // s.Targ = s.Targ.Intersect(s.Dest.Bounds()) // This check should be done by the rasterizer?
  133. // s.r.Draw(s.Dest, s.Targ, s.Source, s.Offset)
  134. }
  135. // Clear cancels any previous accumulated scans
  136. func (s *ScannerGV) Clear() {
  137. p := s.r.Size()
  138. s.r.Reset(p.X, p.Y)
  139. const mxfi = fixed.Int26_6(math.MaxInt32)
  140. s.minX, s.minY, s.maxX, s.maxY = mxfi, mxfi, -mxfi, -mxfi
  141. }
  142. // SetBounds sets the maximum width and height of the rasterized image and
  143. // calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
  144. func (s *ScannerGV) SetBounds(width, height int) {
  145. s.r.Reset(width, height)
  146. }
  147. // NewScannerGV creates a new Scanner with the given bounds.
  148. func NewScannerGV(width, height int, dest draw.Image,
  149. targ image.Rectangle) *ScannerGV {
  150. s := new(ScannerGV)
  151. s.SetBounds(width, height)
  152. s.Dest = dest
  153. s.Targ = targ
  154. s.clipImage = &ClipImage{}
  155. s.clipImage.ColorFuncImage.Uniform.C = &color.RGBA{255, 0, 0, 255}
  156. s.Source = &s.clipImage.ColorFuncImage.Uniform
  157. s.Offset = image.Point{0, 0}
  158. return s
  159. }