vector.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:generate go run gen.go
  5. //go:generate asmfmt -w acc_amd64.s
  6. // asmfmt is https://github.com/klauspost/asmfmt
  7. // Package vector provides a rasterizer for 2-D vector graphics.
  8. package vector // import "golang.org/x/image/vector"
  9. // The rasterizer's design follows
  10. // https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445
  11. //
  12. // Proof of concept code is in
  13. // https://github.com/google/font-go
  14. //
  15. // See also:
  16. // http://nothings.org/gamedev/rasterize/
  17. // http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
  18. // https://people.gnome.org/~mathieu/libart/internals.html#INTERNALS-SCANLINE
  19. import (
  20. "image"
  21. "image/color"
  22. "image/draw"
  23. "math"
  24. )
  25. // floatingPointMathThreshold is the width or height above which the rasterizer
  26. // chooses to used floating point math instead of fixed point math.
  27. //
  28. // Both implementations of line segmentation rasterization (see raster_fixed.go
  29. // and raster_floating.go) implement the same algorithm (in ideal, infinite
  30. // precision math) but they perform differently in practice. The fixed point
  31. // math version is roughtly 1.25x faster (on GOARCH=amd64) on the benchmarks,
  32. // but at sufficiently large scales, the computations will overflow and hence
  33. // show rendering artifacts. The floating point math version has more
  34. // consistent quality over larger scales, but it is significantly slower.
  35. //
  36. // This constant determines when to use the faster implementation and when to
  37. // use the better quality implementation.
  38. //
  39. // The rationale for this particular value is that TestRasterizePolygon in
  40. // vector_test.go checks the rendering quality of polygon edges at various
  41. // angles, inscribed in a circle of diameter 512. It may be that a higher value
  42. // would still produce acceptable quality, but 512 seems to work.
  43. const floatingPointMathThreshold = 512
  44. func lerp(t, px, py, qx, qy float32) (x, y float32) {
  45. return px + t*(qx-px), py + t*(qy-py)
  46. }
  47. func clamp(i, width int32) uint {
  48. if i < 0 {
  49. return 0
  50. }
  51. if i < width {
  52. return uint(i)
  53. }
  54. return uint(width)
  55. }
  56. // NewRasterizer returns a new Rasterizer whose rendered mask image is bounded
  57. // by the given width and height.
  58. func NewRasterizer(w, h int) *Rasterizer {
  59. z := &Rasterizer{}
  60. z.Reset(w, h)
  61. return z
  62. }
  63. // Raster is a 2-D vector graphics rasterizer.
  64. //
  65. // The zero value is usable, in that it is a Rasterizer whose rendered mask
  66. // image has zero width and zero height. Call Reset to change its bounds.
  67. type Rasterizer struct {
  68. // bufXxx are buffers of float32 or uint32 values, holding either the
  69. // individual or cumulative area values.
  70. //
  71. // We don't actually need both values at any given time, and to conserve
  72. // memory, the integration of the individual to the cumulative could modify
  73. // the buffer in place. In other words, we could use a single buffer, say
  74. // of type []uint32, and add some math.Float32bits and math.Float32frombits
  75. // calls to satisfy the compiler's type checking. As of Go 1.7, though,
  76. // there is a performance penalty between:
  77. // bufF32[i] += x
  78. // and
  79. // bufU32[i] = math.Float32bits(x + math.Float32frombits(bufU32[i]))
  80. //
  81. // See golang.org/issue/17220 for some discussion.
  82. bufF32 []float32
  83. bufU32 []uint32
  84. useFloatingPointMath bool
  85. size image.Point
  86. firstX float32
  87. firstY float32
  88. penX float32
  89. penY float32
  90. // DrawOp is the operator used for the Draw method.
  91. //
  92. // The zero value is draw.Over.
  93. DrawOp draw.Op
  94. // TODO: an exported field equivalent to the mask point in the
  95. // draw.DrawMask function in the stdlib image/draw package?
  96. }
  97. // Reset resets a Rasterizer as if it was just returned by NewRasterizer.
  98. //
  99. // This includes setting z.DrawOp to draw.Over.
  100. func (z *Rasterizer) Reset(w, h int) {
  101. z.size = image.Point{w, h}
  102. z.firstX = 0
  103. z.firstY = 0
  104. z.penX = 0
  105. z.penY = 0
  106. z.DrawOp = draw.Over
  107. z.setUseFloatingPointMath(w > floatingPointMathThreshold || h > floatingPointMathThreshold)
  108. }
  109. func (z *Rasterizer) setUseFloatingPointMath(b bool) {
  110. z.useFloatingPointMath = b
  111. // Make z.bufF32 or z.bufU32 large enough to hold width * height samples.
  112. if z.useFloatingPointMath {
  113. if n := z.size.X * z.size.Y; n > cap(z.bufF32) {
  114. z.bufF32 = make([]float32, n)
  115. } else {
  116. z.bufF32 = z.bufF32[:n]
  117. for i := range z.bufF32 {
  118. z.bufF32[i] = 0
  119. }
  120. }
  121. } else {
  122. if n := z.size.X * z.size.Y; n > cap(z.bufU32) {
  123. z.bufU32 = make([]uint32, n)
  124. } else {
  125. z.bufU32 = z.bufU32[:n]
  126. for i := range z.bufU32 {
  127. z.bufU32[i] = 0
  128. }
  129. }
  130. }
  131. }
  132. // Size returns the width and height passed to NewRasterizer or Reset.
  133. func (z *Rasterizer) Size() image.Point {
  134. return z.size
  135. }
  136. // Bounds returns the rectangle from (0, 0) to the width and height passed to
  137. // NewRasterizer or Reset.
  138. func (z *Rasterizer) Bounds() image.Rectangle {
  139. return image.Rectangle{Max: z.size}
  140. }
  141. // Pen returns the location of the path-drawing pen: the last argument to the
  142. // most recent XxxTo call.
  143. func (z *Rasterizer) Pen() (x, y float32) {
  144. return z.penX, z.penY
  145. }
  146. // ClosePath closes the current path.
  147. func (z *Rasterizer) ClosePath() {
  148. z.LineTo(z.firstX, z.firstY)
  149. }
  150. // MoveTo starts a new path and moves the pen to (ax, ay).
  151. //
  152. // The coordinates are allowed to be out of the Rasterizer's bounds.
  153. func (z *Rasterizer) MoveTo(ax, ay float32) {
  154. z.firstX = ax
  155. z.firstY = ay
  156. z.penX = ax
  157. z.penY = ay
  158. }
  159. // LineTo adds a line segment, from the pen to (bx, by), and moves the pen to
  160. // (bx, by).
  161. //
  162. // The coordinates are allowed to be out of the Rasterizer's bounds.
  163. func (z *Rasterizer) LineTo(bx, by float32) {
  164. if z.useFloatingPointMath {
  165. z.floatingLineTo(bx, by)
  166. } else {
  167. z.fixedLineTo(bx, by)
  168. }
  169. }
  170. // QuadTo adds a quadratic Bézier segment, from the pen via (bx, by) to (cx,
  171. // cy), and moves the pen to (cx, cy).
  172. //
  173. // The coordinates are allowed to be out of the Rasterizer's bounds.
  174. func (z *Rasterizer) QuadTo(bx, by, cx, cy float32) {
  175. ax, ay := z.penX, z.penY
  176. devsq := devSquared(ax, ay, bx, by, cx, cy)
  177. if devsq >= 0.333 {
  178. const tol = 3
  179. n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
  180. t, nInv := float32(0), 1/float32(n)
  181. for i := 0; i < n-1; i++ {
  182. t += nInv
  183. abx, aby := lerp(t, ax, ay, bx, by)
  184. bcx, bcy := lerp(t, bx, by, cx, cy)
  185. z.LineTo(lerp(t, abx, aby, bcx, bcy))
  186. }
  187. }
  188. z.LineTo(cx, cy)
  189. }
  190. // CubeTo adds a cubic Bézier segment, from the pen via (bx, by) and (cx, cy)
  191. // to (dx, dy), and moves the pen to (dx, dy).
  192. //
  193. // The coordinates are allowed to be out of the Rasterizer's bounds.
  194. func (z *Rasterizer) CubeTo(bx, by, cx, cy, dx, dy float32) {
  195. ax, ay := z.penX, z.penY
  196. devsq := devSquared(ax, ay, bx, by, dx, dy)
  197. if devsqAlt := devSquared(ax, ay, cx, cy, dx, dy); devsq < devsqAlt {
  198. devsq = devsqAlt
  199. }
  200. if devsq >= 0.333 {
  201. const tol = 3
  202. n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
  203. t, nInv := float32(0), 1/float32(n)
  204. for i := 0; i < n-1; i++ {
  205. t += nInv
  206. abx, aby := lerp(t, ax, ay, bx, by)
  207. bcx, bcy := lerp(t, bx, by, cx, cy)
  208. cdx, cdy := lerp(t, cx, cy, dx, dy)
  209. abcx, abcy := lerp(t, abx, aby, bcx, bcy)
  210. bcdx, bcdy := lerp(t, bcx, bcy, cdx, cdy)
  211. z.LineTo(lerp(t, abcx, abcy, bcdx, bcdy))
  212. }
  213. }
  214. z.LineTo(dx, dy)
  215. }
  216. // devSquared returns a measure of how curvy the sequence (ax, ay) to (bx, by)
  217. // to (cx, cy) is. It determines how many line segments will approximate a
  218. // Bézier curve segment.
  219. //
  220. // http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html
  221. // gives the rationale for this evenly spaced heuristic instead of a recursive
  222. // de Casteljau approach:
  223. //
  224. // The reason for the subdivision by n is that I expect the "flatness"
  225. // computation to be semi-expensive (it's done once rather than on each
  226. // potential subdivision) and also because you'll often get fewer subdivisions.
  227. // Taking a circular arc as a simplifying assumption (ie a spherical cow),
  228. // where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't
  229. // made any horrible mistakes, is expected to be 33% more in the limit.
  230. func devSquared(ax, ay, bx, by, cx, cy float32) float32 {
  231. devx := ax - 2*bx + cx
  232. devy := ay - 2*by + cy
  233. return devx*devx + devy*devy
  234. }
  235. // Draw implements the Drawer interface from the standard library's image/draw
  236. // package.
  237. //
  238. // The vector paths previously added via the XxxTo calls become the mask for
  239. // drawing src onto dst.
  240. func (z *Rasterizer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
  241. // TODO: adjust r and sp (and mp?) if src.Bounds() doesn't contain
  242. // r.Add(sp.Sub(r.Min)).
  243. if src, ok := src.(*image.Uniform); ok {
  244. srcR, srcG, srcB, srcA := src.RGBA()
  245. switch dst := dst.(type) {
  246. case *image.Alpha:
  247. // Fast path for glyph rendering.
  248. if srcA == 0xffff {
  249. if z.DrawOp == draw.Over {
  250. z.rasterizeDstAlphaSrcOpaqueOpOver(dst, r)
  251. } else {
  252. z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
  253. }
  254. return
  255. }
  256. case *image.RGBA:
  257. if z.DrawOp == draw.Over {
  258. z.rasterizeDstRGBASrcUniformOpOver(dst, r, srcR, srcG, srcB, srcA)
  259. } else {
  260. z.rasterizeDstRGBASrcUniformOpSrc(dst, r, srcR, srcG, srcB, srcA)
  261. }
  262. return
  263. }
  264. }
  265. if z.DrawOp == draw.Over {
  266. z.rasterizeOpOver(dst, r, src, sp)
  267. } else {
  268. z.rasterizeOpSrc(dst, r, src, sp)
  269. }
  270. }
  271. func (z *Rasterizer) accumulateMask() {
  272. if z.useFloatingPointMath {
  273. if n := z.size.X * z.size.Y; n > cap(z.bufU32) {
  274. z.bufU32 = make([]uint32, n)
  275. } else {
  276. z.bufU32 = z.bufU32[:n]
  277. }
  278. if haveAccumulateSIMD {
  279. floatingAccumulateMaskSIMD(z.bufU32, z.bufF32)
  280. } else {
  281. floatingAccumulateMask(z.bufU32, z.bufF32)
  282. }
  283. } else {
  284. if haveAccumulateSIMD {
  285. fixedAccumulateMaskSIMD(z.bufU32)
  286. } else {
  287. fixedAccumulateMask(z.bufU32)
  288. }
  289. }
  290. }
  291. func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpOver(dst *image.Alpha, r image.Rectangle) {
  292. // TODO: non-zero vs even-odd winding?
  293. if r == dst.Bounds() && r == z.Bounds() {
  294. // We bypass the z.accumulateMask step and convert straight from
  295. // z.bufF32 or z.bufU32 to dst.Pix.
  296. if z.useFloatingPointMath {
  297. if haveAccumulateSIMD {
  298. floatingAccumulateOpOverSIMD(dst.Pix, z.bufF32)
  299. } else {
  300. floatingAccumulateOpOver(dst.Pix, z.bufF32)
  301. }
  302. } else {
  303. if haveAccumulateSIMD {
  304. fixedAccumulateOpOverSIMD(dst.Pix, z.bufU32)
  305. } else {
  306. fixedAccumulateOpOver(dst.Pix, z.bufU32)
  307. }
  308. }
  309. return
  310. }
  311. z.accumulateMask()
  312. pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
  313. for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
  314. for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
  315. ma := z.bufU32[y*z.size.X+x]
  316. i := y*dst.Stride + x
  317. // This formula is like rasterizeOpOver's, simplified for the
  318. // concrete dst type and opaque src assumption.
  319. a := 0xffff - ma
  320. pix[i] = uint8((uint32(pix[i])*0x101*a/0xffff + ma) >> 8)
  321. }
  322. }
  323. }
  324. func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) {
  325. // TODO: non-zero vs even-odd winding?
  326. if r == dst.Bounds() && r == z.Bounds() {
  327. // We bypass the z.accumulateMask step and convert straight from
  328. // z.bufF32 or z.bufU32 to dst.Pix.
  329. if z.useFloatingPointMath {
  330. if haveAccumulateSIMD {
  331. floatingAccumulateOpSrcSIMD(dst.Pix, z.bufF32)
  332. } else {
  333. floatingAccumulateOpSrc(dst.Pix, z.bufF32)
  334. }
  335. } else {
  336. if haveAccumulateSIMD {
  337. fixedAccumulateOpSrcSIMD(dst.Pix, z.bufU32)
  338. } else {
  339. fixedAccumulateOpSrc(dst.Pix, z.bufU32)
  340. }
  341. }
  342. return
  343. }
  344. z.accumulateMask()
  345. pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
  346. for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
  347. for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
  348. ma := z.bufU32[y*z.size.X+x]
  349. // This formula is like rasterizeOpSrc's, simplified for the
  350. // concrete dst type and opaque src assumption.
  351. pix[y*dst.Stride+x] = uint8(ma >> 8)
  352. }
  353. }
  354. }
  355. func (z *Rasterizer) rasterizeDstRGBASrcUniformOpOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
  356. z.accumulateMask()
  357. pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
  358. for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
  359. for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
  360. ma := z.bufU32[y*z.size.X+x]
  361. // This formula is like rasterizeOpOver's, simplified for the
  362. // concrete dst type and uniform src assumption.
  363. a := 0xffff - (sa * ma / 0xffff)
  364. i := y*dst.Stride + 4*x
  365. pix[i+0] = uint8(((uint32(pix[i+0])*0x101*a + sr*ma) / 0xffff) >> 8)
  366. pix[i+1] = uint8(((uint32(pix[i+1])*0x101*a + sg*ma) / 0xffff) >> 8)
  367. pix[i+2] = uint8(((uint32(pix[i+2])*0x101*a + sb*ma) / 0xffff) >> 8)
  368. pix[i+3] = uint8(((uint32(pix[i+3])*0x101*a + sa*ma) / 0xffff) >> 8)
  369. }
  370. }
  371. }
  372. func (z *Rasterizer) rasterizeDstRGBASrcUniformOpSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
  373. z.accumulateMask()
  374. pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
  375. for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
  376. for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
  377. ma := z.bufU32[y*z.size.X+x]
  378. // This formula is like rasterizeOpSrc's, simplified for the
  379. // concrete dst type and uniform src assumption.
  380. i := y*dst.Stride + 4*x
  381. pix[i+0] = uint8((sr * ma / 0xffff) >> 8)
  382. pix[i+1] = uint8((sg * ma / 0xffff) >> 8)
  383. pix[i+2] = uint8((sb * ma / 0xffff) >> 8)
  384. pix[i+3] = uint8((sa * ma / 0xffff) >> 8)
  385. }
  386. }
  387. }
  388. func (z *Rasterizer) rasterizeOpOver(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
  389. z.accumulateMask()
  390. out := color.RGBA64{}
  391. outc := color.Color(&out)
  392. for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
  393. for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
  394. sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
  395. ma := z.bufU32[y*z.size.X+x]
  396. // This algorithm comes from the standard library's image/draw
  397. // package.
  398. dr, dg, db, da := dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
  399. a := 0xffff - (sa * ma / 0xffff)
  400. out.R = uint16((dr*a + sr*ma) / 0xffff)
  401. out.G = uint16((dg*a + sg*ma) / 0xffff)
  402. out.B = uint16((db*a + sb*ma) / 0xffff)
  403. out.A = uint16((da*a + sa*ma) / 0xffff)
  404. dst.Set(r.Min.X+x, r.Min.Y+y, outc)
  405. }
  406. }
  407. }
  408. func (z *Rasterizer) rasterizeOpSrc(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
  409. z.accumulateMask()
  410. out := color.RGBA64{}
  411. outc := color.Color(&out)
  412. for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
  413. for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
  414. sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
  415. ma := z.bufU32[y*z.size.X+x]
  416. // This algorithm comes from the standard library's image/draw
  417. // package.
  418. out.R = uint16(sr * ma / 0xffff)
  419. out.G = uint16(sg * ma / 0xffff)
  420. out.B = uint16(sb * ma / 0xffff)
  421. out.A = uint16(sa * ma / 0xffff)
  422. dst.Set(r.Min.X+x, r.Min.Y+y, outc)
  423. }
  424. }
  425. }