encode.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  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. package iconvg
  5. import (
  6. "errors"
  7. "image/color"
  8. "math"
  9. "golang.org/x/image/math/f32"
  10. )
  11. var (
  12. errCSELUsedAsBothGradientAndStop = errors.New("iconvg: CSEL used as both gradient and stop")
  13. errDrawingOpsUsedInStylingMode = errors.New("iconvg: drawing ops used in styling mode")
  14. errInvalidSelectorAdjustment = errors.New("iconvg: invalid selector adjustment")
  15. errInvalidIncrementingAdjustment = errors.New("iconvg: invalid incrementing adjustment")
  16. errStylingOpsUsedInDrawingMode = errors.New("iconvg: styling ops used in drawing mode")
  17. errTooManyGradientStops = errors.New("iconvg: too many gradient stops")
  18. )
  19. type mode uint8
  20. const (
  21. modeInitial mode = iota
  22. modeStyling
  23. modeDrawing
  24. )
  25. // Encoder is an IconVG encoder.
  26. //
  27. // The zero value is usable. Calling Reset, which is optional, sets the
  28. // Metadata for the subsequent encoded form. If Reset is not called before
  29. // other Encoder methods, the default metadata is implied.
  30. //
  31. // It aims to emit byte-identical Bytes output for the same input, independent
  32. // of the platform (and specifically its floating-point hardware).
  33. type Encoder struct {
  34. // HighResolutionCoordinates is whether the encoder should encode
  35. // coordinate numbers for subsequent paths at the best possible resolution
  36. // afforded by the underlying graphic format.
  37. //
  38. // By default (false), the encoder quantizes coordinates to 1/64th of a
  39. // unit if possible (the default graphic size is 64 by 64 units, so
  40. // 1/4096th of the default width or height). Each such coordinate can
  41. // therefore be encoded in either 1 or 2 bytes. If true, some coordinates
  42. // will be encoded in 4 bytes, giving greater accuracy but larger file
  43. // sizes. On the Material Design icon set, the 950 or so icons take up
  44. // around 40% more bytes (172K vs 123K) at high resolution.
  45. //
  46. // See the package documentation for more details on the coordinate number
  47. // encoding format.
  48. HighResolutionCoordinates bool
  49. // highResolutionCoordinates is a local copy, copied during StartPath, to
  50. // avoid having to specify the semantics of modifying the exported field
  51. // while drawing.
  52. highResolutionCoordinates bool
  53. buf buffer
  54. altBuf buffer
  55. metadata Metadata
  56. err error
  57. lod0 float32
  58. lod1 float32
  59. cSel uint8
  60. nSel uint8
  61. mode mode
  62. drawOp byte
  63. drawArgs []float32
  64. scratch [12]byte
  65. }
  66. // Bytes returns the encoded form.
  67. func (e *Encoder) Bytes() ([]byte, error) {
  68. if e.err != nil {
  69. return nil, e.err
  70. }
  71. if e.mode == modeInitial {
  72. e.appendDefaultMetadata()
  73. }
  74. return []byte(e.buf), nil
  75. }
  76. // Reset resets the Encoder for the given Metadata.
  77. //
  78. // This includes setting e.HighResolutionCoordinates to false.
  79. func (e *Encoder) Reset(m Metadata) {
  80. *e = Encoder{
  81. buf: append(e.buf[:0], magic...),
  82. metadata: m,
  83. mode: modeStyling,
  84. lod1: positiveInfinity,
  85. }
  86. nMetadataChunks := 0
  87. mcViewBox := m.ViewBox != DefaultViewBox
  88. if mcViewBox {
  89. nMetadataChunks++
  90. }
  91. mcSuggestedPalette := m.Palette != DefaultPalette
  92. if mcSuggestedPalette {
  93. nMetadataChunks++
  94. }
  95. e.buf.encodeNatural(uint32(nMetadataChunks))
  96. if mcViewBox {
  97. e.altBuf = e.altBuf[:0]
  98. e.altBuf.encodeNatural(midViewBox)
  99. e.altBuf.encodeCoordinate(m.ViewBox.Min[0])
  100. e.altBuf.encodeCoordinate(m.ViewBox.Min[1])
  101. e.altBuf.encodeCoordinate(m.ViewBox.Max[0])
  102. e.altBuf.encodeCoordinate(m.ViewBox.Max[1])
  103. e.buf.encodeNatural(uint32(len(e.altBuf)))
  104. e.buf = append(e.buf, e.altBuf...)
  105. }
  106. if mcSuggestedPalette {
  107. n := 63
  108. for ; n >= 0 && m.Palette[n] == (color.RGBA{0x00, 0x00, 0x00, 0xff}); n-- {
  109. }
  110. // Find the shortest encoding that can represent all of m.Palette's n+1
  111. // explicit colors.
  112. enc1, enc2, enc3 := true, true, true
  113. for _, c := range m.Palette[:n+1] {
  114. if enc1 && (!is1(c.R) || !is1(c.G) || !is1(c.B) || !is1(c.A)) {
  115. enc1 = false
  116. }
  117. if enc2 && (!is2(c.R) || !is2(c.G) || !is2(c.B) || !is2(c.A)) {
  118. enc2 = false
  119. }
  120. if enc3 && (c.A != 0xff) {
  121. enc3 = false
  122. }
  123. }
  124. e.altBuf = e.altBuf[:0]
  125. e.altBuf.encodeNatural(midSuggestedPalette)
  126. if enc1 {
  127. e.altBuf = append(e.altBuf, byte(n)|0x00)
  128. for _, c := range m.Palette[:n+1] {
  129. x, _ := encodeColor1(RGBAColor(c))
  130. e.altBuf = append(e.altBuf, x)
  131. }
  132. } else if enc2 {
  133. e.altBuf = append(e.altBuf, byte(n)|0x40)
  134. for _, c := range m.Palette[:n+1] {
  135. x, _ := encodeColor2(RGBAColor(c))
  136. e.altBuf = append(e.altBuf, x[0], x[1])
  137. }
  138. } else if enc3 {
  139. e.altBuf = append(e.altBuf, byte(n)|0x80)
  140. for _, c := range m.Palette[:n+1] {
  141. e.altBuf = append(e.altBuf, c.R, c.G, c.B)
  142. }
  143. } else {
  144. e.altBuf = append(e.altBuf, byte(n)|0xc0)
  145. for _, c := range m.Palette[:n+1] {
  146. e.altBuf = append(e.altBuf, c.R, c.G, c.B, c.A)
  147. }
  148. }
  149. e.buf.encodeNatural(uint32(len(e.altBuf)))
  150. e.buf = append(e.buf, e.altBuf...)
  151. }
  152. }
  153. func (e *Encoder) appendDefaultMetadata() {
  154. e.buf = append(e.buf[:0], magic...)
  155. e.buf = append(e.buf, 0x00) // There are zero metadata chunks.
  156. e.mode = modeStyling
  157. }
  158. func (e *Encoder) CSel() uint8 {
  159. if e.mode == modeInitial {
  160. e.appendDefaultMetadata()
  161. }
  162. return e.cSel
  163. }
  164. func (e *Encoder) NSel() uint8 {
  165. if e.mode == modeInitial {
  166. e.appendDefaultMetadata()
  167. }
  168. return e.nSel
  169. }
  170. func (e *Encoder) LOD() (lod0, lod1 float32) {
  171. if e.mode == modeInitial {
  172. e.appendDefaultMetadata()
  173. }
  174. return e.lod0, e.lod1
  175. }
  176. func (e *Encoder) checkModeStyling() {
  177. if e.mode == modeStyling {
  178. return
  179. }
  180. if e.mode == modeInitial {
  181. e.appendDefaultMetadata()
  182. return
  183. }
  184. e.err = errStylingOpsUsedInDrawingMode
  185. }
  186. func (e *Encoder) SetCSel(cSel uint8) {
  187. e.checkModeStyling()
  188. if e.err != nil {
  189. return
  190. }
  191. e.cSel = cSel & 0x3f
  192. e.buf = append(e.buf, e.cSel)
  193. }
  194. func (e *Encoder) SetNSel(nSel uint8) {
  195. e.checkModeStyling()
  196. if e.err != nil {
  197. return
  198. }
  199. e.nSel = nSel & 0x3f
  200. e.buf = append(e.buf, e.nSel|0x40)
  201. }
  202. func (e *Encoder) SetCReg(adj uint8, incr bool, c Color) {
  203. e.checkModeStyling()
  204. if e.err != nil {
  205. return
  206. }
  207. if adj > 6 {
  208. e.err = errInvalidSelectorAdjustment
  209. return
  210. }
  211. if incr {
  212. if adj != 0 {
  213. e.err = errInvalidIncrementingAdjustment
  214. }
  215. adj = 7
  216. }
  217. if x, ok := encodeColor1(c); ok {
  218. e.buf = append(e.buf, adj|0x80, x)
  219. return
  220. }
  221. if x, ok := encodeColor2(c); ok {
  222. e.buf = append(e.buf, adj|0x88, x[0], x[1])
  223. return
  224. }
  225. if x, ok := encodeColor3Direct(c); ok {
  226. e.buf = append(e.buf, adj|0x90, x[0], x[1], x[2])
  227. return
  228. }
  229. if x, ok := encodeColor4(c); ok {
  230. e.buf = append(e.buf, adj|0x98, x[0], x[1], x[2], x[3])
  231. return
  232. }
  233. if x, ok := encodeColor3Indirect(c); ok {
  234. e.buf = append(e.buf, adj|0xa0, x[0], x[1], x[2])
  235. return
  236. }
  237. panic("unreachable")
  238. }
  239. func (e *Encoder) SetNReg(adj uint8, incr bool, f float32) {
  240. e.checkModeStyling()
  241. if e.err != nil {
  242. return
  243. }
  244. if adj > 6 {
  245. e.err = errInvalidSelectorAdjustment
  246. return
  247. }
  248. if incr {
  249. if adj != 0 {
  250. e.err = errInvalidIncrementingAdjustment
  251. }
  252. adj = 7
  253. }
  254. // Try three different encodings and pick the shortest.
  255. b := buffer(e.scratch[0:0])
  256. opcode, iBest, nBest := uint8(0xa8), 0, b.encodeReal(f)
  257. b = buffer(e.scratch[4:4])
  258. if n := b.encodeCoordinate(f); n < nBest {
  259. opcode, iBest, nBest = 0xb0, 4, n
  260. }
  261. b = buffer(e.scratch[8:8])
  262. if n := b.encodeZeroToOne(f); n < nBest {
  263. opcode, iBest, nBest = 0xb8, 8, n
  264. }
  265. e.buf = append(e.buf, adj|opcode)
  266. e.buf = append(e.buf, e.scratch[iBest:iBest+nBest]...)
  267. }
  268. func (e *Encoder) SetLOD(lod0, lod1 float32) {
  269. e.checkModeStyling()
  270. if e.err != nil {
  271. return
  272. }
  273. e.lod0 = lod0
  274. e.lod1 = lod1
  275. e.buf = append(e.buf, 0xc7)
  276. e.buf.encodeReal(lod0)
  277. e.buf.encodeReal(lod1)
  278. }
  279. // SetGradient sets CREG[CSEL] to encode the gradient whose colors defined by
  280. // spread and stops. Its geometry is either linear or radial, depending on the
  281. // radial argument, and the given affine transformation matrix maps from
  282. // graphic coordinate space defined by the metadata's viewBox (e.g. from (-32,
  283. // -32) to (+32, +32)) to gradient coordinate space. Gradient coordinate space
  284. // is where a linear gradient ranges from x=0 to x=1, and a radial gradient has
  285. // center (0, 0) and radius 1.
  286. //
  287. // The colors of the n stops are encoded at CREG[cBase+0], CREG[cBase+1], ...,
  288. // CREG[cBase+n-1]. Similarly, the offsets of the n stops are encoded at
  289. // NREG[nBase+0], NREG[nBase+1], ..., NREG[nBase+n-1]. Additional parameters
  290. // are stored at NREG[nBase-4], NREG[nBase-3], NREG[nBase-2] and NREG[nBase-1].
  291. //
  292. // The CSEL and NSEL selector registers maintain the same values after the
  293. // method returns as they had when the method was called.
  294. //
  295. // See the package documentation for more details on the gradient encoding
  296. // format and the derivation of common transformation matrices.
  297. func (e *Encoder) SetGradient(cBase, nBase uint8, radial bool, transform f32.Aff3, spread GradientSpread, stops []GradientStop) {
  298. e.checkModeStyling()
  299. if e.err != nil {
  300. return
  301. }
  302. if len(stops) > 64-len(transform) {
  303. e.err = errTooManyGradientStops
  304. return
  305. }
  306. if x, y := e.cSel, e.cSel+64; (cBase <= x && x < cBase+uint8(len(stops))) ||
  307. (cBase <= y && y < cBase+uint8(len(stops))) {
  308. e.err = errCSELUsedAsBothGradientAndStop
  309. return
  310. }
  311. oldCSel := e.cSel
  312. oldNSel := e.nSel
  313. cBase &= 0x3f
  314. nBase &= 0x3f
  315. bFlags := uint8(0x80)
  316. if radial {
  317. bFlags = 0xc0
  318. }
  319. e.SetCReg(0, false, RGBAColor(color.RGBA{
  320. R: uint8(len(stops)),
  321. G: cBase | uint8(spread<<6),
  322. B: nBase | bFlags,
  323. A: 0x00,
  324. }))
  325. e.SetCSel(cBase)
  326. e.SetNSel(nBase)
  327. for i, v := range transform {
  328. e.SetNReg(uint8(len(transform)-i), false, v)
  329. }
  330. for _, s := range stops {
  331. r, g, b, a := s.Color.RGBA()
  332. e.SetCReg(0, true, RGBAColor(color.RGBA{
  333. R: uint8(r >> 8),
  334. G: uint8(g >> 8),
  335. B: uint8(b >> 8),
  336. A: uint8(a >> 8),
  337. }))
  338. e.SetNReg(0, true, s.Offset)
  339. }
  340. e.SetCSel(oldCSel)
  341. e.SetNSel(oldNSel)
  342. }
  343. // SetLinearGradient is like SetGradient with radial=false except that the
  344. // transformation matrix is implicitly defined by two boundary points (x1, y1)
  345. // and (x2, y2).
  346. func (e *Encoder) SetLinearGradient(cBase, nBase uint8, x1, y1, x2, y2 float32, spread GradientSpread, stops []GradientStop) {
  347. // See the package documentation's appendix for a derivation of the
  348. // transformation matrix.
  349. dx, dy := x2-x1, y2-y1
  350. d := dx*dx + dy*dy
  351. ma := dx / d
  352. mb := dy / d
  353. e.SetGradient(cBase, nBase, false, f32.Aff3{
  354. ma, mb, -ma*x1 - mb*y1,
  355. 0, 0, 0,
  356. }, spread, stops)
  357. }
  358. // SetCircularGradient is like SetGradient with radial=true except that the
  359. // transformation matrix is implicitly defined by a center (cx, cy) and a
  360. // radius vector (rx, ry) such that (cx+rx, cy+ry) is on the circle.
  361. func (e *Encoder) SetCircularGradient(cBase, nBase uint8, cx, cy, rx, ry float32, spread GradientSpread, stops []GradientStop) {
  362. // See the package documentation's appendix for a derivation of the
  363. // transformation matrix.
  364. invR := float32(1 / math.Sqrt(float64(rx*rx+ry*ry)))
  365. e.SetGradient(cBase, nBase, true, f32.Aff3{
  366. invR, 0, -cx * invR,
  367. 0, invR, -cy * invR,
  368. }, spread, stops)
  369. }
  370. // SetEllipticalGradient is like SetGradient with radial=true except that the
  371. // transformation matrix is implicitly defined by a center (cx, cy) and two
  372. // axis vectors (rx, ry) and (sx, sy) such that (cx+rx, cy+ry) and (cx+sx,
  373. // cy+sy) are on the ellipse.
  374. func (e *Encoder) SetEllipticalGradient(cBase, nBase uint8, cx, cy, rx, ry, sx, sy float32, spread GradientSpread, stops []GradientStop) {
  375. // Explicitly disable FMA in the floating-point calculations below
  376. // to get consistent results on all platforms, and in turn produce
  377. // a byte-identical encoding.
  378. // See https://golang.org/ref/spec#Floating_point_operators and issue 43219.
  379. // See the package documentation's appendix for a derivation of the
  380. // transformation matrix.
  381. invRSSR := 1 / (float32(rx*sy) - float32(sx*ry))
  382. ma := +sy * invRSSR
  383. mb := -sx * invRSSR
  384. mc := -float32(ma*cx) - float32(mb*cy)
  385. md := -ry * invRSSR
  386. me := +rx * invRSSR
  387. mf := -float32(md*cx) - float32(me*cy)
  388. e.SetGradient(cBase, nBase, true, f32.Aff3{
  389. ma, mb, mc,
  390. md, me, mf,
  391. }, spread, stops)
  392. }
  393. func (e *Encoder) StartPath(adj uint8, x, y float32) {
  394. e.checkModeStyling()
  395. if e.err != nil {
  396. return
  397. }
  398. if adj > 6 {
  399. e.err = errInvalidSelectorAdjustment
  400. return
  401. }
  402. e.highResolutionCoordinates = e.HighResolutionCoordinates
  403. e.buf = append(e.buf, uint8(0xc0+adj))
  404. e.buf.encodeCoordinate(quantize(x, e.highResolutionCoordinates))
  405. e.buf.encodeCoordinate(quantize(y, e.highResolutionCoordinates))
  406. e.mode = modeDrawing
  407. }
  408. func (e *Encoder) AbsHLineTo(x float32) { e.draw('H', x, 0, 0, 0, 0, 0) }
  409. func (e *Encoder) RelHLineTo(x float32) { e.draw('h', x, 0, 0, 0, 0, 0) }
  410. func (e *Encoder) AbsVLineTo(y float32) { e.draw('V', y, 0, 0, 0, 0, 0) }
  411. func (e *Encoder) RelVLineTo(y float32) { e.draw('v', y, 0, 0, 0, 0, 0) }
  412. func (e *Encoder) AbsLineTo(x, y float32) { e.draw('L', x, y, 0, 0, 0, 0) }
  413. func (e *Encoder) RelLineTo(x, y float32) { e.draw('l', x, y, 0, 0, 0, 0) }
  414. func (e *Encoder) AbsSmoothQuadTo(x, y float32) { e.draw('T', x, y, 0, 0, 0, 0) }
  415. func (e *Encoder) RelSmoothQuadTo(x, y float32) { e.draw('t', x, y, 0, 0, 0, 0) }
  416. func (e *Encoder) AbsQuadTo(x1, y1, x, y float32) { e.draw('Q', x1, y1, x, y, 0, 0) }
  417. func (e *Encoder) RelQuadTo(x1, y1, x, y float32) { e.draw('q', x1, y1, x, y, 0, 0) }
  418. func (e *Encoder) AbsSmoothCubeTo(x2, y2, x, y float32) { e.draw('S', x2, y2, x, y, 0, 0) }
  419. func (e *Encoder) RelSmoothCubeTo(x2, y2, x, y float32) { e.draw('s', x2, y2, x, y, 0, 0) }
  420. func (e *Encoder) AbsCubeTo(x1, y1, x2, y2, x, y float32) { e.draw('C', x1, y1, x2, y2, x, y) }
  421. func (e *Encoder) RelCubeTo(x1, y1, x2, y2, x, y float32) { e.draw('c', x1, y1, x2, y2, x, y) }
  422. func (e *Encoder) ClosePathEndPath() { e.draw('Z', 0, 0, 0, 0, 0, 0) }
  423. func (e *Encoder) ClosePathAbsMoveTo(x, y float32) { e.draw('Y', x, y, 0, 0, 0, 0) }
  424. func (e *Encoder) ClosePathRelMoveTo(x, y float32) { e.draw('y', x, y, 0, 0, 0, 0) }
  425. func (e *Encoder) AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
  426. e.arcTo('A', rx, ry, xAxisRotation, largeArc, sweep, x, y)
  427. }
  428. func (e *Encoder) RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
  429. e.arcTo('a', rx, ry, xAxisRotation, largeArc, sweep, x, y)
  430. }
  431. func (e *Encoder) arcTo(drawOp byte, rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
  432. flags := uint32(0)
  433. if largeArc {
  434. flags |= 0x01
  435. }
  436. if sweep {
  437. flags |= 0x02
  438. }
  439. e.draw(drawOp, rx, ry, xAxisRotation, float32(flags), x, y)
  440. }
  441. func (e *Encoder) draw(drawOp byte, arg0, arg1, arg2, arg3, arg4, arg5 float32) {
  442. if e.err != nil {
  443. return
  444. }
  445. if e.mode != modeDrawing {
  446. e.err = errDrawingOpsUsedInStylingMode
  447. return
  448. }
  449. if e.drawOp != drawOp {
  450. e.flushDrawOps()
  451. }
  452. e.drawOp = drawOp
  453. switch drawOps[drawOp].nArgs {
  454. case 0:
  455. // No-op.
  456. case 1:
  457. e.drawArgs = append(e.drawArgs, arg0)
  458. case 2:
  459. e.drawArgs = append(e.drawArgs, arg0, arg1)
  460. case 4:
  461. e.drawArgs = append(e.drawArgs, arg0, arg1, arg2, arg3)
  462. case 6:
  463. e.drawArgs = append(e.drawArgs, arg0, arg1, arg2, arg3, arg4, arg5)
  464. default:
  465. panic("unreachable")
  466. }
  467. switch drawOp {
  468. case 'Z':
  469. e.mode = modeStyling
  470. fallthrough
  471. case 'Y', 'y':
  472. e.flushDrawOps()
  473. }
  474. }
  475. func (e *Encoder) flushDrawOps() {
  476. if e.drawOp == 0x00 {
  477. return
  478. }
  479. if op := drawOps[e.drawOp]; op.nArgs == 0 {
  480. e.buf = append(e.buf, op.opcodeBase)
  481. } else {
  482. n := len(e.drawArgs) / int(op.nArgs)
  483. for i := 0; n > 0; {
  484. m := n
  485. if m > int(op.maxRepCount) {
  486. m = int(op.maxRepCount)
  487. }
  488. e.buf = append(e.buf, op.opcodeBase+uint8(m)-1)
  489. switch e.drawOp {
  490. default:
  491. for j := m * int(op.nArgs); j > 0; j-- {
  492. e.buf.encodeCoordinate(quantize(e.drawArgs[i], e.highResolutionCoordinates))
  493. i++
  494. }
  495. case 'A', 'a':
  496. for j := m; j > 0; j-- {
  497. e.buf.encodeCoordinate(quantize(e.drawArgs[i+0], e.highResolutionCoordinates))
  498. e.buf.encodeCoordinate(quantize(e.drawArgs[i+1], e.highResolutionCoordinates))
  499. e.buf.encodeAngle(e.drawArgs[i+2])
  500. e.buf.encodeNatural(uint32(e.drawArgs[i+3]))
  501. e.buf.encodeCoordinate(quantize(e.drawArgs[i+4], e.highResolutionCoordinates))
  502. e.buf.encodeCoordinate(quantize(e.drawArgs[i+5], e.highResolutionCoordinates))
  503. i += 6
  504. }
  505. }
  506. n -= m
  507. }
  508. }
  509. e.drawOp = 0x00
  510. e.drawArgs = e.drawArgs[:0]
  511. }
  512. func quantize(coord float32, highResolutionCoordinates bool) float32 {
  513. if !highResolutionCoordinates && (-128 <= coord && coord < 128) {
  514. x := math.Floor(float64(coord*64 + 0.5))
  515. return float32(x) / 64
  516. }
  517. return coord
  518. }
  519. var drawOps = [256]struct {
  520. opcodeBase byte
  521. maxRepCount uint8
  522. nArgs uint8
  523. }{
  524. 'L': {0x00, 32, 2},
  525. 'l': {0x20, 32, 2},
  526. 'T': {0x40, 16, 2},
  527. 't': {0x50, 16, 2},
  528. 'Q': {0x60, 16, 4},
  529. 'q': {0x70, 16, 4},
  530. 'S': {0x80, 16, 4},
  531. 's': {0x90, 16, 4},
  532. 'C': {0xa0, 16, 6},
  533. 'c': {0xb0, 16, 6},
  534. 'A': {0xc0, 16, 6},
  535. 'a': {0xd0, 16, 6},
  536. // Z means close path and then end path.
  537. 'Z': {0xe1, 1, 0},
  538. // Y/y means close path and then open a new path (with a MoveTo/moveTo).
  539. 'Y': {0xe2, 1, 2},
  540. 'y': {0xe3, 1, 2},
  541. 'H': {0xe6, 1, 1},
  542. 'h': {0xe7, 1, 1},
  543. 'V': {0xe8, 1, 1},
  544. 'v': {0xe9, 1, 1},
  545. }