decode.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  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. "bytes"
  7. "errors"
  8. "image/color"
  9. )
  10. var (
  11. errInconsistentMetadataChunkLength = errors.New("iconvg: inconsistent metadata chunk length")
  12. errInvalidColor = errors.New("iconvg: invalid color")
  13. errInvalidMagicIdentifier = errors.New("iconvg: invalid magic identifier")
  14. errInvalidMetadataChunkLength = errors.New("iconvg: invalid metadata chunk length")
  15. errInvalidMetadataIdentifier = errors.New("iconvg: invalid metadata identifier")
  16. errInvalidNumber = errors.New("iconvg: invalid number")
  17. errInvalidNumberOfMetadataChunks = errors.New("iconvg: invalid number of metadata chunks")
  18. errInvalidSuggestedPalette = errors.New("iconvg: invalid suggested palette")
  19. errInvalidViewBox = errors.New("iconvg: invalid view box")
  20. errUnsupportedDrawingOpcode = errors.New("iconvg: unsupported drawing opcode")
  21. errUnsupportedMetadataIdentifier = errors.New("iconvg: unsupported metadata identifier")
  22. errUnsupportedStylingOpcode = errors.New("iconvg: unsupported styling opcode")
  23. errUnsupportedUpgrade = errors.New("iconvg: unsupported upgrade")
  24. )
  25. var midDescriptions = [...]string{
  26. midViewBox: "viewBox",
  27. midSuggestedPalette: "suggested palette",
  28. }
  29. // Destination handles the actions decoded from an IconVG graphic's opcodes.
  30. //
  31. // When passed to Decode, the first method called (if any) will be Reset. No
  32. // methods will be called at all if an error is encountered in the encoded form
  33. // before the metadata is fully decoded.
  34. type Destination interface {
  35. Reset(m Metadata)
  36. SetCSel(cSel uint8)
  37. SetNSel(nSel uint8)
  38. SetCReg(adj uint8, incr bool, c Color)
  39. SetNReg(adj uint8, incr bool, f float32)
  40. SetLOD(lod0, lod1 float32)
  41. StartPath(adj uint8, x, y float32)
  42. ClosePathEndPath()
  43. ClosePathAbsMoveTo(x, y float32)
  44. ClosePathRelMoveTo(x, y float32)
  45. AbsHLineTo(x float32)
  46. RelHLineTo(x float32)
  47. AbsVLineTo(y float32)
  48. RelVLineTo(y float32)
  49. AbsLineTo(x, y float32)
  50. RelLineTo(x, y float32)
  51. AbsSmoothQuadTo(x, y float32)
  52. RelSmoothQuadTo(x, y float32)
  53. AbsQuadTo(x1, y1, x, y float32)
  54. RelQuadTo(x1, y1, x, y float32)
  55. AbsSmoothCubeTo(x2, y2, x, y float32)
  56. RelSmoothCubeTo(x2, y2, x, y float32)
  57. AbsCubeTo(x1, y1, x2, y2, x, y float32)
  58. RelCubeTo(x1, y1, x2, y2, x, y float32)
  59. AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32)
  60. RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32)
  61. }
  62. type printer func(b []byte, format string, args ...interface{})
  63. // DecodeOptions are the optional parameters to the Decode function.
  64. type DecodeOptions struct {
  65. // Palette is an optional 64 color palette. If one isn't provided, the
  66. // IconVG graphic's suggested palette will be used.
  67. Palette *Palette
  68. }
  69. // DecodeMetadata decodes only the metadata in an IconVG graphic.
  70. func DecodeMetadata(src []byte) (m Metadata, err error) {
  71. m.ViewBox = DefaultViewBox
  72. m.Palette = DefaultPalette
  73. if err = decode(nil, nil, &m, true, src, nil); err != nil {
  74. return Metadata{}, err
  75. }
  76. return m, nil
  77. }
  78. // Decode decodes an IconVG graphic.
  79. func Decode(dst Destination, src []byte, opts *DecodeOptions) error {
  80. m := Metadata{
  81. ViewBox: DefaultViewBox,
  82. Palette: DefaultPalette,
  83. }
  84. if opts != nil && opts.Palette != nil {
  85. m.Palette = *opts.Palette
  86. }
  87. return decode(dst, nil, &m, false, src, opts)
  88. }
  89. func decode(dst Destination, p printer, m *Metadata, metadataOnly bool, src buffer, opts *DecodeOptions) (err error) {
  90. if !bytes.HasPrefix(src, magicBytes) {
  91. // TODO: detect FFV 1 (File Format Version 1), as opposed to the FFV 0
  92. // that this package implements, and delegate to a FFV 1 decoder.
  93. return errInvalidMagicIdentifier
  94. }
  95. if p != nil {
  96. p(src[:len(magic)], "IconVG Magic identifier\n")
  97. }
  98. src = src[len(magic):]
  99. nMetadataChunks, n := src.decodeNatural()
  100. if n == 0 {
  101. return errInvalidNumberOfMetadataChunks
  102. }
  103. if p != nil {
  104. p(src[:n], "Number of metadata chunks: %d\n", nMetadataChunks)
  105. }
  106. src = src[n:]
  107. for ; nMetadataChunks > 0; nMetadataChunks-- {
  108. src, err = decodeMetadataChunk(p, m, src, opts)
  109. if err != nil {
  110. return err
  111. }
  112. }
  113. if metadataOnly {
  114. return nil
  115. }
  116. if dst != nil {
  117. dst.Reset(*m)
  118. }
  119. mf := modeFunc(decodeStyling)
  120. for len(src) > 0 {
  121. mf, src, err = mf(dst, p, src)
  122. if err != nil {
  123. return err
  124. }
  125. }
  126. return nil
  127. }
  128. func decodeMetadataChunk(p printer, m *Metadata, src buffer, opts *DecodeOptions) (src1 buffer, err error) {
  129. length, n := src.decodeNatural()
  130. if n == 0 {
  131. return nil, errInvalidMetadataChunkLength
  132. }
  133. if p != nil {
  134. p(src[:n], "Metadata chunk length: %d\n", length)
  135. }
  136. src = src[n:]
  137. lenSrcWant := int64(len(src)) - int64(length)
  138. mid, n := src.decodeNatural()
  139. if n == 0 {
  140. return nil, errInvalidMetadataIdentifier
  141. }
  142. if mid >= uint32(len(midDescriptions)) {
  143. return nil, errUnsupportedMetadataIdentifier
  144. }
  145. if p != nil {
  146. p(src[:n], "Metadata Identifier: %d (%s)\n", mid, midDescriptions[mid])
  147. }
  148. src = src[n:]
  149. switch mid {
  150. case midViewBox:
  151. if m.ViewBox.Min[0], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
  152. return nil, errInvalidViewBox
  153. }
  154. if m.ViewBox.Min[1], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
  155. return nil, errInvalidViewBox
  156. }
  157. if m.ViewBox.Max[0], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
  158. return nil, errInvalidViewBox
  159. }
  160. if m.ViewBox.Max[1], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
  161. return nil, errInvalidViewBox
  162. }
  163. if m.ViewBox.Min[0] > m.ViewBox.Max[0] || m.ViewBox.Min[1] > m.ViewBox.Max[1] ||
  164. isNaNOrInfinity(m.ViewBox.Min[0]) || isNaNOrInfinity(m.ViewBox.Min[1]) ||
  165. isNaNOrInfinity(m.ViewBox.Max[0]) || isNaNOrInfinity(m.ViewBox.Max[1]) {
  166. return nil, errInvalidViewBox
  167. }
  168. case midSuggestedPalette:
  169. if len(src) == 0 {
  170. return nil, errInvalidSuggestedPalette
  171. }
  172. length, format := 1+int(src[0]&0x3f), src[0]>>6
  173. decode := buffer.decodeColor4
  174. switch format {
  175. case 0:
  176. decode = buffer.decodeColor1
  177. case 1:
  178. decode = buffer.decodeColor2
  179. case 2:
  180. decode = buffer.decodeColor3Direct
  181. }
  182. if p != nil {
  183. p(src[:1], " %d palette colors, %d bytes per color\n", length, 1+format)
  184. }
  185. src = src[1:]
  186. for i := 0; i < length; i++ {
  187. c, n := decode(src)
  188. if n == 0 {
  189. return nil, errInvalidSuggestedPalette
  190. }
  191. rgba := c.rgba()
  192. if c.typ != ColorTypeRGBA || !validAlphaPremulColor(rgba) {
  193. rgba = color.RGBA{0x00, 0x00, 0x00, 0xff}
  194. }
  195. if p != nil {
  196. p(src[:n], " RGBA %02x%02x%02x%02x\n", rgba.R, rgba.G, rgba.B, rgba.A)
  197. }
  198. src = src[n:]
  199. if opts == nil || opts.Palette == nil {
  200. m.Palette[i] = rgba
  201. }
  202. }
  203. default:
  204. return nil, errUnsupportedMetadataIdentifier
  205. }
  206. if int64(len(src)) != lenSrcWant {
  207. return nil, errInconsistentMetadataChunkLength
  208. }
  209. return src, nil
  210. }
  211. // modeFunc is the decoding mode: whether we are decoding styling or drawing
  212. // opcodes.
  213. //
  214. // It is a function type. The decoding loop calls this function to decode and
  215. // execute the next opcode from the src buffer, returning the subsequent mode
  216. // and the remaining source bytes.
  217. type modeFunc func(dst Destination, p printer, src buffer) (modeFunc, buffer, error)
  218. func decodeStyling(dst Destination, p printer, src buffer) (modeFunc, buffer, error) {
  219. switch opcode := src[0]; {
  220. case opcode < 0x80:
  221. if opcode < 0x40 {
  222. opcode &= 0x3f
  223. if p != nil {
  224. p(src[:1], "Set CSEL = %d\n", opcode)
  225. }
  226. src = src[1:]
  227. if dst != nil {
  228. dst.SetCSel(opcode)
  229. }
  230. } else {
  231. opcode &= 0x3f
  232. if p != nil {
  233. p(src[:1], "Set NSEL = %d\n", opcode)
  234. }
  235. src = src[1:]
  236. if dst != nil {
  237. dst.SetNSel(opcode)
  238. }
  239. }
  240. return decodeStyling, src, nil
  241. case opcode < 0xa8:
  242. return decodeSetCReg(dst, p, src, opcode)
  243. case opcode < 0xc0:
  244. return decodeSetNReg(dst, p, src, opcode)
  245. case opcode < 0xc7:
  246. return decodeStartPath(dst, p, src, opcode)
  247. case opcode == 0xc7:
  248. return decodeSetLOD(dst, p, src)
  249. }
  250. return nil, nil, errUnsupportedStylingOpcode
  251. }
  252. func decodeSetCReg(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
  253. nBytes, directness, adj := 0, "", opcode&0x07
  254. var decode func(buffer) (Color, int)
  255. incr := adj == 7
  256. if incr {
  257. adj = 0
  258. }
  259. switch (opcode - 0x80) >> 3 {
  260. case 0:
  261. nBytes, directness, decode = 1, "", buffer.decodeColor1
  262. case 1:
  263. nBytes, directness, decode = 2, "", buffer.decodeColor2
  264. case 2:
  265. nBytes, directness, decode = 3, " (direct)", buffer.decodeColor3Direct
  266. case 3:
  267. nBytes, directness, decode = 4, "", buffer.decodeColor4
  268. case 4:
  269. nBytes, directness, decode = 3, " (indirect)", buffer.decodeColor3Indirect
  270. }
  271. if p != nil {
  272. if incr {
  273. p(src[:1], "Set CREG[CSEL-0] to a %d byte%s color; CSEL++\n", nBytes, directness)
  274. } else {
  275. p(src[:1], "Set CREG[CSEL-%d] to a %d byte%s color\n", adj, nBytes, directness)
  276. }
  277. }
  278. src = src[1:]
  279. c, n := decode(src)
  280. if n == 0 {
  281. return nil, nil, errInvalidColor
  282. }
  283. if p != nil {
  284. printColor(src[:n], p, c, "")
  285. }
  286. src = src[n:]
  287. if dst != nil {
  288. dst.SetCReg(adj, incr, c)
  289. }
  290. return decodeStyling, src, nil
  291. }
  292. func printColor(src []byte, p printer, c Color, prefix string) {
  293. switch c.typ {
  294. case ColorTypeRGBA:
  295. if rgba := c.rgba(); validAlphaPremulColor(rgba) {
  296. p(src, " %sRGBA %02x%02x%02x%02x\n", prefix, rgba.R, rgba.G, rgba.B, rgba.A)
  297. } else if rgba.A == 0 && rgba.B&0x80 != 0 {
  298. p(src, " %sgradient (NSTOPS=%d, CBASE=%d, NBASE=%d, %s, %s)\n",
  299. prefix,
  300. rgba.R&0x3f,
  301. rgba.G&0x3f,
  302. rgba.B&0x3f,
  303. gradientShapeNames[(rgba.B>>6)&0x01],
  304. gradientSpreadNames[rgba.G>>6],
  305. )
  306. } else {
  307. p(src, " %snonsensical color\n", prefix)
  308. }
  309. case ColorTypePaletteIndex:
  310. p(src, " %scustomPalette[%d]\n", prefix, c.paletteIndex())
  311. case ColorTypeCReg:
  312. p(src, " %sCREG[%d]\n", prefix, c.cReg())
  313. case ColorTypeBlend:
  314. t, c0, c1 := c.blend()
  315. p(src[:1], " blend %d:%d c0:c1\n", 0xff-t, t)
  316. printColor(src[1:2], p, decodeColor1(c0), " c0: ")
  317. printColor(src[2:3], p, decodeColor1(c1), " c1: ")
  318. }
  319. }
  320. func decodeSetNReg(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
  321. decode, typ, adj := buffer.decodeZeroToOne, "zero-to-one", opcode&0x07
  322. incr := adj == 7
  323. if incr {
  324. adj = 0
  325. }
  326. switch (opcode - 0xa8) >> 3 {
  327. case 0:
  328. decode, typ = buffer.decodeReal, "real"
  329. case 1:
  330. decode, typ = buffer.decodeCoordinate, "coordinate"
  331. }
  332. if p != nil {
  333. if incr {
  334. p(src[:1], "Set NREG[NSEL-0] to a %s number; NSEL++\n", typ)
  335. } else {
  336. p(src[:1], "Set NREG[NSEL-%d] to a %s number\n", adj, typ)
  337. }
  338. }
  339. src = src[1:]
  340. f, n := decode(src)
  341. if n == 0 {
  342. return nil, nil, errInvalidNumber
  343. }
  344. if p != nil {
  345. p(src[:n], " %g\n", f)
  346. }
  347. src = src[n:]
  348. if dst != nil {
  349. dst.SetNReg(adj, incr, f)
  350. }
  351. return decodeStyling, src, nil
  352. }
  353. func decodeStartPath(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
  354. adj := opcode & 0x07
  355. if p != nil {
  356. p(src[:1], "Start path, filled with CREG[CSEL-%d]; M (absolute moveTo)\n", adj)
  357. }
  358. src = src[1:]
  359. x, src, err := decodeNumber(p, src, buffer.decodeCoordinate)
  360. if err != nil {
  361. return nil, nil, err
  362. }
  363. y, src, err := decodeNumber(p, src, buffer.decodeCoordinate)
  364. if err != nil {
  365. return nil, nil, err
  366. }
  367. if dst != nil {
  368. dst.StartPath(adj, x, y)
  369. }
  370. return decodeDrawing, src, nil
  371. }
  372. func decodeSetLOD(dst Destination, p printer, src buffer) (modeFunc, buffer, error) {
  373. if p != nil {
  374. p(src[:1], "Set LOD\n")
  375. }
  376. src = src[1:]
  377. lod0, src, err := decodeNumber(p, src, buffer.decodeReal)
  378. if err != nil {
  379. return nil, nil, err
  380. }
  381. lod1, src, err := decodeNumber(p, src, buffer.decodeReal)
  382. if err != nil {
  383. return nil, nil, err
  384. }
  385. if dst != nil {
  386. dst.SetLOD(lod0, lod1)
  387. }
  388. return decodeStyling, src, nil
  389. }
  390. func decodeDrawing(dst Destination, p printer, src buffer) (mf modeFunc, src1 buffer, err error) {
  391. var coords [6]float32
  392. switch opcode := src[0]; {
  393. case opcode < 0xe0:
  394. op, nCoords, nReps := "", 0, 1+int(opcode&0x0f)
  395. switch opcode >> 4 {
  396. case 0x00, 0x01:
  397. op = "L (absolute lineTo)"
  398. nCoords = 2
  399. nReps = 1 + int(opcode&0x1f)
  400. case 0x02, 0x03:
  401. op = "l (relative lineTo)"
  402. nCoords = 2
  403. nReps = 1 + int(opcode&0x1f)
  404. case 0x04:
  405. op = "T (absolute smooth quadTo)"
  406. nCoords = 2
  407. case 0x05:
  408. op = "t (relative smooth quadTo)"
  409. nCoords = 2
  410. case 0x06:
  411. op = "Q (absolute quadTo)"
  412. nCoords = 4
  413. case 0x07:
  414. op = "q (relative quadTo)"
  415. nCoords = 4
  416. case 0x08:
  417. op = "S (absolute smooth cubeTo)"
  418. nCoords = 4
  419. case 0x09:
  420. op = "s (relative smooth cubeTo)"
  421. nCoords = 4
  422. case 0x0a:
  423. op = "C (absolute cubeTo)"
  424. nCoords = 6
  425. case 0x0b:
  426. op = "c (relative cubeTo)"
  427. nCoords = 6
  428. case 0x0c:
  429. op = "A (absolute arcTo)"
  430. nCoords = 0
  431. case 0x0d:
  432. op = "a (relative arcTo)"
  433. nCoords = 0
  434. }
  435. if p != nil {
  436. p(src[:1], "%s, %d reps\n", op, nReps)
  437. }
  438. src = src[1:]
  439. for i := 0; i < nReps; i++ {
  440. if p != nil && i != 0 {
  441. p(nil, "%s, implicit\n", op)
  442. }
  443. var largeArc, sweep bool
  444. if op[0] != 'A' && op[0] != 'a' {
  445. src, err = decodeCoordinates(coords[:nCoords], p, src)
  446. if err != nil {
  447. return nil, nil, err
  448. }
  449. } else {
  450. // We have an absolute or relative arcTo.
  451. src, err = decodeCoordinates(coords[:2], p, src)
  452. if err != nil {
  453. return nil, nil, err
  454. }
  455. coords[2], src, err = decodeAngle(p, src)
  456. if err != nil {
  457. return nil, nil, err
  458. }
  459. largeArc, sweep, src, err = decodeArcToFlags(p, src)
  460. if err != nil {
  461. return nil, nil, err
  462. }
  463. src, err = decodeCoordinates(coords[4:6], p, src)
  464. if err != nil {
  465. return nil, nil, err
  466. }
  467. }
  468. if dst == nil {
  469. continue
  470. }
  471. switch op[0] {
  472. case 'L':
  473. dst.AbsLineTo(coords[0], coords[1])
  474. case 'l':
  475. dst.RelLineTo(coords[0], coords[1])
  476. case 'T':
  477. dst.AbsSmoothQuadTo(coords[0], coords[1])
  478. case 't':
  479. dst.RelSmoothQuadTo(coords[0], coords[1])
  480. case 'Q':
  481. dst.AbsQuadTo(coords[0], coords[1], coords[2], coords[3])
  482. case 'q':
  483. dst.RelQuadTo(coords[0], coords[1], coords[2], coords[3])
  484. case 'S':
  485. dst.AbsSmoothCubeTo(coords[0], coords[1], coords[2], coords[3])
  486. case 's':
  487. dst.RelSmoothCubeTo(coords[0], coords[1], coords[2], coords[3])
  488. case 'C':
  489. dst.AbsCubeTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])
  490. case 'c':
  491. dst.RelCubeTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])
  492. case 'A':
  493. dst.AbsArcTo(coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
  494. case 'a':
  495. dst.RelArcTo(coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
  496. }
  497. }
  498. case opcode == 0xe1:
  499. if p != nil {
  500. p(src[:1], "z (closePath); end path\n")
  501. }
  502. src = src[1:]
  503. if dst != nil {
  504. dst.ClosePathEndPath()
  505. }
  506. return decodeStyling, src, nil
  507. case opcode == 0xe2:
  508. if p != nil {
  509. p(src[:1], "z (closePath); M (absolute moveTo)\n")
  510. }
  511. src = src[1:]
  512. src, err = decodeCoordinates(coords[:2], p, src)
  513. if err != nil {
  514. return nil, nil, err
  515. }
  516. if dst != nil {
  517. dst.ClosePathAbsMoveTo(coords[0], coords[1])
  518. }
  519. case opcode == 0xe3:
  520. if p != nil {
  521. p(src[:1], "z (closePath); m (relative moveTo)\n")
  522. }
  523. src = src[1:]
  524. src, err = decodeCoordinates(coords[:2], p, src)
  525. if err != nil {
  526. return nil, nil, err
  527. }
  528. if dst != nil {
  529. dst.ClosePathRelMoveTo(coords[0], coords[1])
  530. }
  531. case opcode == 0xe6:
  532. if p != nil {
  533. p(src[:1], "H (absolute horizontal lineTo)\n")
  534. }
  535. src = src[1:]
  536. src, err = decodeCoordinates(coords[:1], p, src)
  537. if err != nil {
  538. return nil, nil, err
  539. }
  540. if dst != nil {
  541. dst.AbsHLineTo(coords[0])
  542. }
  543. case opcode == 0xe7:
  544. if p != nil {
  545. p(src[:1], "h (relative horizontal lineTo)\n")
  546. }
  547. src = src[1:]
  548. src, err = decodeCoordinates(coords[:1], p, src)
  549. if err != nil {
  550. return nil, nil, err
  551. }
  552. if dst != nil {
  553. dst.RelHLineTo(coords[0])
  554. }
  555. case opcode == 0xe8:
  556. if p != nil {
  557. p(src[:1], "V (absolute vertical lineTo)\n")
  558. }
  559. src = src[1:]
  560. src, err = decodeCoordinates(coords[:1], p, src)
  561. if err != nil {
  562. return nil, nil, err
  563. }
  564. if dst != nil {
  565. dst.AbsVLineTo(coords[0])
  566. }
  567. case opcode == 0xe9:
  568. if p != nil {
  569. p(src[:1], "v (relative vertical lineTo)\n")
  570. }
  571. src = src[1:]
  572. src, err = decodeCoordinates(coords[:1], p, src)
  573. if err != nil {
  574. return nil, nil, err
  575. }
  576. if dst != nil {
  577. dst.RelVLineTo(coords[0])
  578. }
  579. default:
  580. return nil, nil, errUnsupportedDrawingOpcode
  581. }
  582. return decodeDrawing, src, nil
  583. }
  584. type decodeNumberFunc func(buffer) (float32, int)
  585. func decodeNumber(p printer, src buffer, dnf decodeNumberFunc) (float32, buffer, error) {
  586. x, n := dnf(src)
  587. if n == 0 {
  588. return 0, nil, errInvalidNumber
  589. }
  590. if p != nil {
  591. p(src[:n], " %+g\n", x)
  592. }
  593. return x, src[n:], nil
  594. }
  595. func decodeCoordinates(coords []float32, p printer, src buffer) (src1 buffer, err error) {
  596. for i := range coords {
  597. coords[i], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
  598. if err != nil {
  599. return nil, err
  600. }
  601. }
  602. return src, nil
  603. }
  604. func decodeCoordinatePairs(coords [][2]float32, p printer, src buffer) (src1 buffer, err error) {
  605. for i := range coords {
  606. coords[i][0], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
  607. if err != nil {
  608. return nil, err
  609. }
  610. coords[i][1], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
  611. if err != nil {
  612. return nil, err
  613. }
  614. }
  615. return src, nil
  616. }
  617. func decodeAngle(p printer, src buffer) (float32, buffer, error) {
  618. x, n := src.decodeZeroToOne()
  619. if n == 0 {
  620. return 0, nil, errInvalidNumber
  621. }
  622. if p != nil {
  623. p(src[:n], " %v × 360 degrees (%v degrees)\n", x, x*360)
  624. }
  625. return x, src[n:], nil
  626. }
  627. func decodeArcToFlags(p printer, src buffer) (bool, bool, buffer, error) {
  628. x, n := src.decodeNatural()
  629. if n == 0 {
  630. return false, false, nil, errInvalidNumber
  631. }
  632. if p != nil {
  633. p(src[:n], " %#x (largeArc=%d, sweep=%d)\n", x, (x>>0)&0x01, (x>>1)&0x01)
  634. }
  635. return (x>>0)&0x01 != 0, (x>>1)&0x01 != 0, src[n:], nil
  636. }