upgrade.go 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. // Copyright 2021 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. "math"
  10. )
  11. // UpgradeToFileFormatVersion1Options are the options to the
  12. // UpgradeToFileFormatVersion1 function.
  13. type UpgradeToFileFormatVersion1Options struct {
  14. // ArcsExpandWithHighResolutionCoordinates is like the
  15. // Encoder.HighResolutionCoordinates field. It controls whether to favor
  16. // file size (false) or precision (true) when replacing File Format Version
  17. // 0's arcs with cubic Bézier curves.
  18. ArcsExpandWithHighResolutionCoordinates bool
  19. }
  20. // UpgradeToFileFormatVersion1 upgrades IconVG data from the 2016 experimental
  21. // "File Format Version 0" to the 2021 "File Format Version 1".
  22. //
  23. // This package (golang.org/x/exp/shiny/iconvg) holds a decoder for FFV0,
  24. // including this function to convert from FFV0 to FFV1. Different packages
  25. // (github.com/google/iconvg/src/go/*) decode FFV1.
  26. //
  27. // Amongst some new features and other clean-ups, FFV1 sets up the capability
  28. // for animated vector graphics, therefore removing some FFV0 features (such as
  29. // arc segments) that can be hard to animate smoothly. The IconvG FFV1 format
  30. // and its design decisions are discussed at
  31. // https://github.com/google/iconvg/issues/4#issuecomment-874105547
  32. func UpgradeToFileFormatVersion1(v0 []byte, opts *UpgradeToFileFormatVersion1Options) (v1 []byte, retErr error) {
  33. u := &upgrader{}
  34. if opts != nil {
  35. u.opts = *opts
  36. }
  37. for i := range u.creg {
  38. u.creg[i] = upgradeColor{
  39. typ: ColorTypePaletteIndex,
  40. paletteIndex: uint8(i),
  41. }
  42. }
  43. if !bytes.HasPrefix(v0, magicBytes) {
  44. return nil, errInvalidMagicIdentifier
  45. }
  46. v1 = append(v1, "\x8AIVG"...)
  47. v0 = v0[4:]
  48. v1, v0, retErr = u.upgradeMetadata(v1, v0)
  49. if retErr != nil {
  50. return nil, retErr
  51. }
  52. v1, _, retErr = u.upgradeBytecode(v1, v0)
  53. if retErr != nil {
  54. return nil, retErr
  55. }
  56. return v1, nil
  57. }
  58. const (
  59. upgradeVerbMoveTo = 0
  60. upgradeVerbLineTo = 1
  61. upgradeVerbQuadTo = 2
  62. upgradeVerbCubeTo = 3
  63. )
  64. type upgrader struct {
  65. opts UpgradeToFileFormatVersion1Options
  66. // These fields hold the current path's geometry.
  67. verbs []uint8
  68. args [][2]float32
  69. // These fields track most of the FFV0 virtual machine register state. The
  70. // FFV1 register model is different enough that we don't just translate
  71. // each FFV0 register-related opcode individually.
  72. creg [64]upgradeColor
  73. nreg [64]float32
  74. csel uint32
  75. nsel uint32
  76. fill uint32
  77. // These fields track the most recent color written to FFV1 register
  78. // REGS[SEL+7] (and SEL is kept at 56). As a file size optimization, we
  79. // don't have to emit the first half of "Set REGS[SEL+7] = etc; Use
  80. // REGS[SEL+7]" if the register already holds the "etc" value.
  81. regsSel7 color.RGBA
  82. hasRegsSel7 bool
  83. // calculatingJumpLOD is whether the upgrader.upgradeBytecode method is
  84. // being called recursively. FFV0 sets a Level-Of-Detail filter that
  85. // applies implicitly until the next SetLOD opcode (if any). FFV1 instead
  86. // explicitly gives the number of opcodes to skip if outside the LOD range.
  87. calculatingJumpLOD bool
  88. }
  89. func (u *upgrader) upgradeMetadata(v1 buffer, v0 buffer) (newV1 buffer, newV0 buffer, retErr error) {
  90. nMetadataChunks, n := v0.decodeNatural()
  91. if n == 0 {
  92. return nil, nil, errInvalidNumberOfMetadataChunks
  93. }
  94. v1.encodeNaturalFFV1(nMetadataChunks)
  95. v0 = v0[n:]
  96. for ; nMetadataChunks > 0; nMetadataChunks-- {
  97. length, n := v0.decodeNatural()
  98. if n == 0 {
  99. return nil, nil, errInvalidMetadataChunkLength
  100. }
  101. v0 = v0[n:]
  102. if uint64(length) > uint64(len(v0)) {
  103. return nil, nil, errInvalidMetadataChunkLength
  104. }
  105. upgrade, err := u.upgradeMetadataChunk(v0[:length])
  106. if err != nil {
  107. return nil, nil, err
  108. }
  109. v1.encodeNaturalFFV1(uint32(len(upgrade)))
  110. v1 = append(v1, upgrade...)
  111. v0 = v0[length:]
  112. }
  113. return v1, v0, nil
  114. }
  115. func (u *upgrader) upgradeMetadataChunk(v0 buffer) (v1 buffer, retErr error) {
  116. mid, n := v0.decodeNatural()
  117. if n == 0 {
  118. return nil, errInvalidMetadataIdentifier
  119. }
  120. switch mid {
  121. case midViewBox:
  122. mid = ffv1MIDViewBox
  123. case midSuggestedPalette:
  124. mid = ffv1MIDSuggestedPalette
  125. default:
  126. return nil, errInvalidMetadataIdentifier
  127. }
  128. v1.encodeNaturalFFV1(mid)
  129. v0 = v0[n:]
  130. switch mid {
  131. case ffv1MIDViewBox:
  132. for i := 0; i < 4; i++ {
  133. x, n := v0.decodeNatural()
  134. if n == 0 {
  135. return nil, errInvalidViewBox
  136. }
  137. v1.encodeNaturalFFV1(x)
  138. v0 = v0[n:]
  139. }
  140. if len(v0) != 0 {
  141. return nil, errInvalidViewBox
  142. }
  143. case ffv1MIDSuggestedPalette:
  144. if len(v0) == 0 {
  145. return nil, errInvalidSuggestedPalette
  146. }
  147. numColors := 1 + int(v0[0]&0x3f)
  148. colorLength := 1 + int(v0[0]>>6)
  149. v1 = append(v1, uint8(numColors-1))
  150. v0 = v0[1:]
  151. for i := 0; i < numColors; i++ {
  152. c, n := Color{}, 0
  153. switch colorLength {
  154. case 1:
  155. c, n = v0.decodeColor1()
  156. case 2:
  157. c, n = v0.decodeColor2()
  158. case 3:
  159. c, n = v0.decodeColor3Direct()
  160. case 4:
  161. c, n = v0.decodeColor4()
  162. }
  163. if n == 0 {
  164. return nil, errInvalidSuggestedPalette
  165. } else if (c.typ == ColorTypeRGBA) && validAlphaPremulColor(c.data) {
  166. v1 = append(v1, c.data.R, c.data.G, c.data.B, c.data.A)
  167. } else {
  168. v1 = append(v1, 0x00, 0x00, 0x00, 0xff)
  169. }
  170. v0 = v0[n:]
  171. }
  172. if len(v0) != 0 {
  173. return nil, errInvalidSuggestedPalette
  174. }
  175. }
  176. return v1, nil
  177. }
  178. func (u *upgrader) upgradeBytecode(v1 buffer, v0 buffer) (newV1 buffer, newV0 buffer, retErr error) {
  179. uf := upgradeFunc(upgradeStyling)
  180. for len(v0) > 0 {
  181. uf, v1, v0, retErr = uf(u, v1, v0)
  182. if retErr != nil {
  183. if retErr == errCalculatingJumpLOD {
  184. return v1, v0, nil
  185. }
  186. return nil, nil, retErr
  187. }
  188. }
  189. return v1, v0, nil
  190. }
  191. var errCalculatingJumpLOD = errors.New("iconvg: calculating JumpLOD")
  192. type upgradeFunc func(*upgrader, buffer, buffer) (upgradeFunc, buffer, buffer, error)
  193. func upgradeStyling(u *upgrader, v1 buffer, v0 buffer) (uf upgradeFunc, newV1 buffer, newV0 buffer, retErr error) {
  194. for len(v0) > 0 {
  195. switch opcode := v0[0]; {
  196. case opcode < 0x80: // "Set CSEL/NSEL"
  197. if opcode < 0x40 {
  198. u.csel = uint32(opcode & 63)
  199. } else {
  200. u.nsel = uint32(opcode & 63)
  201. }
  202. v0 = v0[1:]
  203. case opcode < 0xa8: // "Set CREG[etc] to an etc color"
  204. adj := uint32(opcode & 7)
  205. if adj == 7 {
  206. adj = 0
  207. }
  208. index := (u.csel - adj) & 63
  209. v0 = v0[1:]
  210. c, n := Color{}, 0
  211. switch (opcode - 0x80) >> 3 {
  212. case 0:
  213. c, n = v0.decodeColor1()
  214. case 1:
  215. c, n = v0.decodeColor2()
  216. case 2:
  217. c, n = v0.decodeColor3Direct()
  218. case 3:
  219. c, n = v0.decodeColor4()
  220. case 4:
  221. c, n = v0.decodeColor3Indirect()
  222. }
  223. if n == 0 {
  224. return nil, nil, nil, errInvalidColor
  225. }
  226. u.creg[index], retErr = u.resolve(c, false)
  227. if retErr != nil {
  228. return nil, nil, nil, retErr
  229. }
  230. v0 = v0[n:]
  231. if (opcode & 7) == 7 {
  232. u.csel = (u.csel + 1) & 63
  233. }
  234. case opcode < 0xc0: // "Set NREG[etc] to a real number"
  235. adj := uint32(opcode & 7)
  236. if adj == 7 {
  237. adj = 0
  238. }
  239. index := (u.nsel - adj) & 63
  240. v0 = v0[1:]
  241. f, n := float32(0), 0
  242. switch (opcode - 0x80) >> 3 {
  243. case 5:
  244. f, n = v0.decodeReal()
  245. case 6:
  246. f, n = v0.decodeCoordinate()
  247. case 7:
  248. f, n = v0.decodeZeroToOne()
  249. }
  250. if n == 0 {
  251. return nil, nil, nil, errInvalidNumber
  252. }
  253. u.nreg[index] = f
  254. v0 = v0[n:]
  255. if (opcode & 7) == 7 {
  256. u.nsel = (u.nsel + 1) & 63
  257. }
  258. case opcode < 0xc7: // Start path.
  259. adj := uint32(opcode & 7)
  260. u.fill = (u.csel - adj) & 63
  261. v1 = append(v1, 0x35) // FFV1 MoveTo.
  262. v0 = v0[1:]
  263. return upgradeDrawing, v1, v0, nil
  264. case opcode == 0xc7: // "Set LOD"
  265. if u.calculatingJumpLOD {
  266. u.calculatingJumpLOD = false
  267. return nil, v1, v0, errCalculatingJumpLOD
  268. }
  269. v0 = v0[1:]
  270. lod := [2]float32{}
  271. for i := range lod {
  272. f, n := v0.decodeReal()
  273. if n == 0 {
  274. return nil, nil, nil, errInvalidNumber
  275. }
  276. lod[i] = f
  277. v0 = v0[n:]
  278. }
  279. if (lod[0] == 0) && math.IsInf(float64(lod[1]), +1) {
  280. break
  281. }
  282. u.calculatingJumpLOD = true
  283. ifTrue := []byte(nil)
  284. if ifTrue, v0, retErr = u.upgradeBytecode(nil, v0); retErr != nil {
  285. return nil, nil, nil, retErr
  286. }
  287. nInstructions := countFFV1Instructions(ifTrue)
  288. if nInstructions >= (1 << 30) {
  289. return nil, nil, nil, errUnsupportedUpgrade
  290. }
  291. v1 = append(v1, 0x3a) // FFV1 JumpLOD.
  292. v1.encodeNaturalFFV1(uint32(nInstructions))
  293. v1.encodeCoordinateFFV1(lod[0])
  294. v1.encodeCoordinateFFV1(lod[1])
  295. v1 = append(v1, ifTrue...)
  296. default:
  297. return nil, nil, nil, errUnsupportedStylingOpcode
  298. }
  299. }
  300. return upgradeStyling, v1, v0, nil
  301. }
  302. func upgradeDrawing(u *upgrader, v1 buffer, v0 buffer) (uf upgradeFunc, newV1 buffer, newV0 buffer, retErr error) {
  303. u.verbs = u.verbs[:0]
  304. u.args = u.args[:0]
  305. coords := [3][2]float32{}
  306. pen := [2]float32{}
  307. prevSmoothType := smoothTypeNone
  308. prevSmoothPoint := [2]float32{}
  309. // Handle the implicit M after a "Start path" styling op.
  310. v0, retErr = decodeCoordinates(pen[:2], nil, v0)
  311. if retErr != nil {
  312. return nil, nil, nil, retErr
  313. }
  314. u.verbs = append(u.verbs, upgradeVerbMoveTo)
  315. u.args = append(u.args, pen)
  316. startingPoint := pen
  317. for len(v0) > 0 {
  318. switch opcode := v0[0]; {
  319. case opcode < 0xc0: // LineTo, QuadTo, CubeTo.
  320. nCoordPairs, nReps, relative, smoothType := 0, 1+int(opcode&0x0f), false, smoothTypeNone
  321. switch opcode >> 4 {
  322. case 0x00, 0x01: // "L (absolute lineTo)"
  323. nCoordPairs = 1
  324. nReps = 1 + int(opcode&0x1f)
  325. case 0x02, 0x03: // "l (relative lineTo)"
  326. nCoordPairs = 1
  327. nReps = 1 + int(opcode&0x1f)
  328. relative = true
  329. case 0x04: // "T (absolute smooth quadTo)"
  330. nCoordPairs = 1
  331. smoothType = smoothTypeQuad
  332. case 0x05: // "t (relative smooth quadTo)"
  333. nCoordPairs = 1
  334. relative = true
  335. smoothType = smoothTypeQuad
  336. case 0x06: // "Q (absolute quadTo)"
  337. nCoordPairs = 2
  338. case 0x07: // "q (relative quadTo)"
  339. nCoordPairs = 2
  340. relative = true
  341. case 0x08: // "S (absolute smooth cubeTo)"
  342. nCoordPairs = 2
  343. smoothType = smoothTypeCube
  344. case 0x09: // "s (relative smooth cubeTo)"
  345. nCoordPairs = 2
  346. relative = true
  347. smoothType = smoothTypeCube
  348. case 0x0a: // "C (absolute cubeTo)"
  349. nCoordPairs = 3
  350. case 0x0b: // "c (relative cubeTo)"
  351. nCoordPairs = 3
  352. relative = true
  353. }
  354. v0 = v0[1:]
  355. for i := 0; i < nReps; i++ {
  356. smoothIndex := 0
  357. if smoothType != smoothTypeNone {
  358. smoothIndex = 1
  359. if smoothType != prevSmoothType {
  360. coords[0][0] = pen[0]
  361. coords[0][1] = pen[1]
  362. } else {
  363. coords[0][0] = (2 * pen[0]) - prevSmoothPoint[0]
  364. coords[0][1] = (2 * pen[1]) - prevSmoothPoint[1]
  365. }
  366. }
  367. allCoords := coords[:smoothIndex+nCoordPairs]
  368. explicitCoords := allCoords[smoothIndex:]
  369. v0, retErr = decodeCoordinatePairs(explicitCoords, nil, v0)
  370. if retErr != nil {
  371. return nil, nil, nil, retErr
  372. }
  373. if relative {
  374. for c := range explicitCoords {
  375. explicitCoords[c][0] += pen[0]
  376. explicitCoords[c][1] += pen[1]
  377. }
  378. }
  379. u.verbs = append(u.verbs, uint8(len(allCoords)))
  380. u.args = append(u.args, allCoords...)
  381. pen = allCoords[len(allCoords)-1]
  382. if len(allCoords) == 2 {
  383. prevSmoothPoint = allCoords[0]
  384. prevSmoothType = smoothTypeQuad
  385. } else if len(allCoords) == 3 {
  386. prevSmoothPoint = allCoords[1]
  387. prevSmoothType = smoothTypeCube
  388. } else {
  389. prevSmoothType = smoothTypeNone
  390. }
  391. }
  392. case opcode < 0xe0: // ArcTo.
  393. v1, v0, retErr = u.upgradeArcs(&pen, v1, v0)
  394. if retErr != nil {
  395. return nil, nil, nil, retErr
  396. }
  397. prevSmoothType = smoothTypeNone
  398. default: // Other drawing opcodes.
  399. v0 = v0[1:]
  400. switch opcode {
  401. case 0xe1: // "z (closePath); end path"
  402. goto endPath
  403. case 0xe2, 0xe3: // "z (closePath); M (absolute/relative moveTo)"
  404. v0, retErr = decodeCoordinatePairs(coords[:1], nil, v0)
  405. if retErr != nil {
  406. return nil, nil, nil, retErr
  407. }
  408. if opcode == 0xe2 {
  409. pen[0] = coords[0][0]
  410. pen[1] = coords[0][1]
  411. } else {
  412. pen[0] += coords[0][0]
  413. pen[1] += coords[0][1]
  414. }
  415. u.verbs = append(u.verbs, upgradeVerbMoveTo)
  416. u.args = append(u.args, pen)
  417. default:
  418. tmp := [1]float32{}
  419. v0, retErr = decodeCoordinates(tmp[:1], nil, v0)
  420. if retErr != nil {
  421. return nil, nil, nil, retErr
  422. }
  423. switch opcode {
  424. case 0xe6: // "H (absolute horizontal lineTo)"
  425. pen[0] = tmp[0]
  426. case 0xe7: // "h (relative horizontal lineTo)"
  427. pen[0] += tmp[0]
  428. case 0xe8: // "V (absolute vertical lineTo)"
  429. pen[1] = tmp[0]
  430. case 0xe9: // "v (relative vertical lineTo)"
  431. pen[1] += tmp[0]
  432. default:
  433. return nil, nil, nil, errUnsupportedDrawingOpcode
  434. }
  435. u.verbs = append(u.verbs, upgradeVerbLineTo)
  436. u.args = append(u.args, pen)
  437. }
  438. prevSmoothType = smoothTypeNone
  439. }
  440. }
  441. endPath:
  442. v1, retErr = u.finishDrawing(v1, startingPoint)
  443. return upgradeStyling, v1, v0, retErr
  444. }
  445. func (u *upgrader) finishDrawing(v1 buffer, startingPoint [2]float32) (newV1 buffer, retErr error) {
  446. v1.encodeCoordinatePairFFV1(u.args[0])
  447. for i, j := 1, 1; i < len(u.verbs); {
  448. curr := u.args[j-1]
  449. runLength := u.computeRunLength(u.verbs[i:])
  450. verb := u.verbs[i]
  451. if verb == upgradeVerbMoveTo {
  452. v1 = append(v1, 0x35) // FFV1 MoveTo.
  453. v1.encodeCoordinatePairFFV1(u.args[j])
  454. i += 1
  455. j += 1
  456. continue
  457. }
  458. switch verb {
  459. case upgradeVerbLineTo:
  460. if ((runLength == 3) && ((j + 3) == len(u.args)) && u.looksLikeParallelogram3(&curr, u.args[j:], &startingPoint)) ||
  461. ((runLength == 4) && u.looksLikeParallelogram4(&curr, u.args[j:j+4])) {
  462. v1 = append(v1, 0x34) // FFV1 Parallelogram.
  463. v1.encodeCoordinatePairFFV1(u.args[j+0])
  464. v1.encodeCoordinatePairFFV1(u.args[j+1])
  465. i += 4
  466. j += 4 * 1
  467. continue
  468. }
  469. case upgradeVerbCubeTo:
  470. if (runLength == 4) && u.looksLikeEllipse(&curr, u.args[j:j+(4*3)]) {
  471. v1 = append(v1, 0x33) // FFV1 Ellipse (4 quarters).
  472. v1.encodeCoordinatePairFFV1(u.args[j+2])
  473. v1.encodeCoordinatePairFFV1(u.args[j+5])
  474. i += 4
  475. j += 4 * 3
  476. continue
  477. }
  478. }
  479. opcodeBase := 0x10 * (verb - 1) // FFV1 LineTo / QuadTo / CubeTo.
  480. if runLength < 16 {
  481. v1 = append(v1, opcodeBase|uint8(runLength))
  482. } else {
  483. v1 = append(v1, opcodeBase)
  484. v1.encodeNaturalFFV1(uint32(runLength) - 16)
  485. }
  486. args := u.args[j : j+(runLength*int(verb))]
  487. for _, arg := range args {
  488. v1.encodeCoordinatePairFFV1(arg)
  489. }
  490. i += runLength
  491. j += len(args)
  492. }
  493. return u.emitFill(v1)
  494. }
  495. func (u *upgrader) emitFill(v1 buffer) (newV1 buffer, retErr error) {
  496. switch c := u.creg[u.fill]; c.typ {
  497. case ColorTypeRGBA:
  498. if validAlphaPremulColor(c.rgba) {
  499. if !u.hasRegsSel7 || (u.regsSel7 != c.rgba) {
  500. u.hasRegsSel7, u.regsSel7 = true, c.rgba
  501. v1 = append(v1, 0x57, // FFV1 Set REGS[SEL+7].hi32.
  502. c.rgba.R, c.rgba.G, c.rgba.B, c.rgba.A)
  503. }
  504. v1 = append(v1, 0x87) // FFV1 Fill (flat color) with REGS[SEL+7].
  505. } else if (c.rgba.A == 0) && (c.rgba.B&0x80 != 0) {
  506. nStops := int(c.rgba.R & 63)
  507. cBase := int(c.rgba.G & 63)
  508. nBase := int(c.rgba.B & 63)
  509. if nStops < 2 {
  510. return nil, errInvalidColor
  511. } else if nStops > 17 {
  512. return nil, errUnsupportedUpgrade
  513. }
  514. v1 = append(v1, 0x70|uint8(nStops-2)) // FFV1 SEL -= N; Set REGS[SEL+1 .. SEL+1+N].
  515. for i := 0; i < nStops; i++ {
  516. if stopOffset := u.nreg[(nBase+i)&63]; stopOffset <= 0 {
  517. v1 = append(v1, 0x00, 0x00, 0x00, 0x00)
  518. } else if stopOffset < 1 {
  519. u := uint32(stopOffset * 0x10000)
  520. v1 = append(v1, uint8(u>>0), uint8(u>>8), uint8(u>>16), uint8(u>>24))
  521. } else {
  522. v1 = append(v1, 0x00, 0x00, 0x01, 0x00)
  523. }
  524. if stopColor := u.creg[(cBase+i)&63]; stopColor.typ != ColorTypeRGBA {
  525. return nil, errUnsupportedUpgrade
  526. } else {
  527. v1 = append(v1,
  528. stopColor.rgba.R,
  529. stopColor.rgba.G,
  530. stopColor.rgba.B,
  531. stopColor.rgba.A,
  532. )
  533. }
  534. }
  535. nMatrixElements := 0
  536. if c.rgba.B&0x40 == 0 {
  537. v1 = append(v1, 0x91, // FFV1 Fill (linear gradient) with REGS[SEL+1 .. SEL+1+N].
  538. (c.rgba.G&0xc0)|uint8(nStops-2))
  539. nMatrixElements = 3
  540. } else {
  541. v1 = append(v1, 0xa1, // FFV1 Fill (radial gradient) with REGS[SEL+1 .. SEL+1+N].
  542. (c.rgba.G&0xc0)|uint8(nStops-2))
  543. nMatrixElements = 6
  544. }
  545. for i := 0; i < nMatrixElements; i++ {
  546. u := math.Float32bits(u.nreg[(nBase+i-6)&63])
  547. v1 = append(v1, uint8(u>>0), uint8(u>>8), uint8(u>>16), uint8(u>>24))
  548. }
  549. v1 = append(v1, 0x36, // FFV1 SEL += N.
  550. uint8(nStops))
  551. } else {
  552. return nil, errInvalidColor
  553. }
  554. case ColorTypePaletteIndex:
  555. if c.paletteIndex < 7 {
  556. v1 = append(v1, 0x88+c.paletteIndex) // FFV1 Fill (flat color) with REGS[SEL+8+N].
  557. } else {
  558. v1 = append(v1, 0x56, // FFV1 Set REGS[SEL+6].hi32.
  559. 0x80|c.paletteIndex, 0, 0, 0,
  560. 0x86) // FFV1 Fill (flat color) with REGS[SEL+6].
  561. }
  562. case ColorTypeBlend:
  563. if c.color0.typ == ColorTypeRGBA {
  564. v1 = append(v1, 0x53, // FFV1 Set REGS[SEL+3].hi32.
  565. c.color0.rgba.R, c.color0.rgba.G, c.color0.rgba.B, c.color0.rgba.A)
  566. }
  567. if c.color1.typ == ColorTypeRGBA {
  568. v1 = append(v1, 0x54, // FFV1 Set REGS[SEL+4].hi32.
  569. c.color1.rgba.R, c.color1.rgba.G, c.color1.rgba.B, c.color1.rgba.A)
  570. }
  571. v1 = append(v1, 0x55, // FFV1 Set REGS[SEL+5].hi32.
  572. c.blend)
  573. if c.color0.typ == ColorTypeRGBA {
  574. v1 = append(v1, 0xfe)
  575. } else {
  576. v1 = append(v1, 0x80|c.color0.paletteIndex)
  577. }
  578. if c.color1.typ == ColorTypeRGBA {
  579. v1 = append(v1, 0xff)
  580. } else {
  581. v1 = append(v1, 0x80|c.color1.paletteIndex)
  582. }
  583. v1 = append(v1, 0, 0x85) // FFV1 Fill (flat color) with REGS[SEL+5].
  584. }
  585. return v1, nil
  586. }
  587. func (u *upgrader) computeRunLength(verbs []uint8) int {
  588. firstVerb := verbs[0]
  589. if firstVerb == 0 {
  590. return 1
  591. }
  592. n := 1
  593. for ; (n < len(verbs)) && (verbs[n] == firstVerb); n++ {
  594. }
  595. return n
  596. }
  597. // looksLikeParallelogram3 is like looksLikeParallelogram4 but the final point
  598. // (implied by the ClosePath op) is separate from the middle 3 args.
  599. func (u *upgrader) looksLikeParallelogram3(curr *[2]float32, args [][2]float32, final *[2]float32) bool {
  600. if len(args) != 3 {
  601. panic("unreachable")
  602. }
  603. return (*curr == *final) &&
  604. (curr[0] == (args[0][0] - args[1][0] + args[2][0])) &&
  605. (curr[1] == (args[0][1] - args[1][1] + args[2][1]))
  606. }
  607. // looksLikeParallelogram4 returns whether the 5 coordinate pairs (A, B, C, D,
  608. // E) form a parallelogram:
  609. //
  610. // E=A B
  611. //
  612. // o---------o
  613. // \ \
  614. // \ \
  615. // \ \
  616. // o---------o
  617. // D C
  618. //
  619. // Specifically, it checks that (A == E) and ((A - B) == (D - C)). That last
  620. // equation can be rearranged as (A == (B - C + D)).
  621. //
  622. // The motivation is that, if looksLikeParallelogram4 is true, then the 5 input
  623. // coordinate pairs can then be compressed to 3: A, B and C. Or, if the current
  624. // point A is implied by context then 4 input pairs can be compressed to 2.
  625. func (u *upgrader) looksLikeParallelogram4(curr *[2]float32, args [][2]float32) bool {
  626. if len(args) != 4 {
  627. panic("unreachable")
  628. }
  629. return (*curr == args[3]) &&
  630. (curr[0] == (args[0][0] - args[1][0] + args[2][0])) &&
  631. (curr[1] == (args[0][1] - args[1][1] + args[2][1]))
  632. }
  633. // looksLikeEllipse returns whether the 13 coordinate pairs (A, A+, B-, B, B+,
  634. // C- C, C+, D-, D, D+, A-, E) form a cubic Bézier approximation to an ellipse.
  635. // Let A± denote the two tangent vectors (A+ - A) and (A - A-) and likewise for
  636. // B±, C± and D±.
  637. //
  638. // A+ B-
  639. //
  640. // E=A o o B
  641. // A- o---------o B+
  642. //
  643. // o \ \ o
  644. // \ X \
  645. // o \ \ o
  646. // D+ o---------o C-
  647. // D o o C
  648. // D- C+
  649. //
  650. // See https://nigeltao.github.io/blog/2021/three-points-define-ellipse.html
  651. // for a better version of that ASCII art.
  652. //
  653. // Specifically, it checks that (A, B, C, D, E), also known as (*curr, args[2],
  654. // args[5], args[8] and args[11]), forms a parallelogram. If so, let X be the
  655. // parallelogram center and define two axis vectors: r = B-X and s = C-X.
  656. //
  657. // These axes define the parallelogram's or ellipse's shape but they are not
  658. // necessarily orthogonal and hence not necessarily the ellipse's major
  659. // (longest) and minor (shortest) axes. If s is a 90 degree rotation of r then
  660. // the parallelogram is a square and the ellipse is a circle.
  661. //
  662. // This function further checks that the A±, B± C± and D± tangents are
  663. // approximately equal to +λ×r, +λ×s, -λ×r and -λ×s, where λ = ((math.Sqrt2 -
  664. // 1) × 4 / 3) comes from the cubic Bézier approximation to a quarter-circle.
  665. //
  666. // The motivation is that, if looksLikeEllipse is true, then the 13 input
  667. // coordinate pairs can then be compressed to 3: A, B and C. Or, if the current
  668. // point A is implied by context then 12 input pairs can be compressed to 2.
  669. func (u *upgrader) looksLikeEllipse(curr *[2]float32, args [][2]float32) bool {
  670. if len(args) != 12 {
  671. panic("unreachable")
  672. }
  673. if (*curr != args[11]) ||
  674. (curr[0] != (args[2][0] - args[5][0] + args[8][0])) ||
  675. (curr[1] != (args[2][1] - args[5][1] + args[8][1])) {
  676. return false
  677. }
  678. center := [2]float32{
  679. (args[2][0] + args[8][0]) / 2,
  680. (args[2][1] + args[8][1]) / 2,
  681. }
  682. // 0.5522847498307933984022516322796 ≈ ((math.Sqrt2 - 1) × 4 / 3), the
  683. // tangent lengths (as a fraction of the radius) for a commonly used cubic
  684. // Bézier approximation to a circle. Multiplying that by 0.98 and 1.02
  685. // checks that we're within 2% of that fraction.
  686. //
  687. // This also covers the slightly different 0.551784777779014 constant,
  688. // recommended by https://pomax.github.io/bezierinfo/#circles_cubic
  689. const λMin = 0.98 * 0.5522847498307933984022516322796
  690. const λMax = 1.02 * 0.5522847498307933984022516322796
  691. // Check the first axis.
  692. r := [2]float32{
  693. args[2][0] - center[0],
  694. args[2][1] - center[1],
  695. }
  696. rMin := [2]float32{r[0] * λMin, r[1] * λMin}
  697. rMax := [2]float32{r[0] * λMax, r[1] * λMax}
  698. if rMin[0] > rMax[0] {
  699. rMin[0], rMax[0] = rMax[0], rMin[0]
  700. }
  701. if rMin[1] > rMax[1] {
  702. rMin[1], rMax[1] = rMax[1], rMin[1]
  703. }
  704. if !within(args[0][0]-curr[0], args[0][1]-curr[1], rMin, rMax) ||
  705. !within(args[4][0]-args[5][0], args[4][1]-args[5][1], rMin, rMax) ||
  706. !within(args[5][0]-args[6][0], args[5][1]-args[6][1], rMin, rMax) ||
  707. !within(args[11][0]-args[10][0], args[11][1]-args[10][1], rMin, rMax) {
  708. return false
  709. }
  710. // Check the second axis.
  711. s := [2]float32{
  712. args[5][0] - center[0],
  713. args[5][1] - center[1],
  714. }
  715. sMin := [2]float32{s[0] * λMin, s[1] * λMin}
  716. sMax := [2]float32{s[0] * λMax, s[1] * λMax}
  717. if sMin[0] > sMax[0] {
  718. sMin[0], sMax[0] = sMax[0], sMin[0]
  719. }
  720. if sMin[1] > sMax[1] {
  721. sMin[1], sMax[1] = sMax[1], sMin[1]
  722. }
  723. if !within(args[2][0]-args[1][0], args[2][1]-args[1][1], sMin, sMax) ||
  724. !within(args[3][0]-args[2][0], args[3][1]-args[2][1], sMin, sMax) ||
  725. !within(args[7][0]-args[8][0], args[7][1]-args[8][1], sMin, sMax) ||
  726. !within(args[8][0]-args[9][0], args[8][1]-args[9][1], sMin, sMax) {
  727. return false
  728. }
  729. return true
  730. }
  731. func within(v0 float32, v1 float32, min [2]float32, max [2]float32) bool {
  732. return (min[0] <= v0) && (v0 <= max[0]) && (min[1] <= v1) && (v1 <= max[1])
  733. }
  734. func (u *upgrader) upgradeArcs(pen *[2]float32, v1 buffer, v0 buffer) (newV1 buffer, newV0 buffer, retErr error) {
  735. coords := [6]float32{}
  736. largeArc, sweep := false, false
  737. opcode := v0[0]
  738. v0 = v0[1:]
  739. nReps := 1 + int(opcode&0x0f)
  740. for i := 0; i < nReps; i++ {
  741. v0, retErr = decodeCoordinates(coords[:2], nil, v0)
  742. if retErr != nil {
  743. return nil, nil, retErr
  744. }
  745. coords[2], v0, retErr = decodeAngle(nil, v0)
  746. if retErr != nil {
  747. return nil, nil, retErr
  748. }
  749. largeArc, sweep, v0, retErr = decodeArcToFlags(nil, v0)
  750. if retErr != nil {
  751. return nil, nil, retErr
  752. }
  753. v0, retErr = decodeCoordinates(coords[4:6], nil, v0)
  754. if retErr != nil {
  755. return nil, nil, retErr
  756. }
  757. if (opcode >> 4) == 0x0d {
  758. coords[4] += pen[0]
  759. coords[5] += pen[1]
  760. }
  761. u.upgradeArc(pen, coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
  762. pen[0] = coords[4]
  763. pen[1] = coords[5]
  764. }
  765. return v1, v0, nil
  766. }
  767. func (u *upgrader) upgradeArc(pen *[2]float32, rx, ry, xAxisRotation float32, largeArc, sweep bool, finalX, finalY float32) {
  768. // We follow the "Conversion from endpoint to center parameterization"
  769. // algorithm as per
  770. // https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
  771. // There seems to be a bug in the spec's "implementation notes".
  772. //
  773. // Actual implementations, such as
  774. // - https://git.gnome.org/browse/librsvg/tree/rsvg-path.c
  775. // - http://svn.apache.org/repos/asf/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/ext/awt/geom/ExtendedGeneralPath.java
  776. // - https://java.net/projects/svgsalamander/sources/svn/content/trunk/svg-core/src/main/java/com/kitfox/svg/pathcmd/Arc.java
  777. // - https://github.com/millermedeiros/SVGParser/blob/master/com/millermedeiros/geom/SVGArc.as
  778. // do something slightly different (marked with a †).
  779. // (†) The Abs isn't part of the spec. Neither is checking that Rx and Ry
  780. // are non-zero (and non-NaN).
  781. Rx := math.Abs(float64(rx))
  782. Ry := math.Abs(float64(ry))
  783. if !(Rx > 0 && Ry > 0) {
  784. u.verbs = append(u.verbs, upgradeVerbLineTo)
  785. u.args = append(u.args, [2]float32{finalX, finalY})
  786. return
  787. }
  788. x1 := float64(pen[0])
  789. y1 := float64(pen[1])
  790. x2 := float64(finalX)
  791. y2 := float64(finalY)
  792. phi := 2 * math.Pi * float64(xAxisRotation)
  793. // Step 1: Compute (x1′, y1′)
  794. halfDx := (x1 - x2) / 2
  795. halfDy := (y1 - y2) / 2
  796. cosPhi := math.Cos(phi)
  797. sinPhi := math.Sin(phi)
  798. x1Prime := +cosPhi*halfDx + sinPhi*halfDy
  799. y1Prime := -sinPhi*halfDx + cosPhi*halfDy
  800. // Step 2: Compute (cx′, cy′)
  801. rxSq := Rx * Rx
  802. rySq := Ry * Ry
  803. x1PrimeSq := x1Prime * x1Prime
  804. y1PrimeSq := y1Prime * y1Prime
  805. // (†) Check that the radii are large enough.
  806. radiiCheck := x1PrimeSq/rxSq + y1PrimeSq/rySq
  807. if radiiCheck > 1 {
  808. c := math.Sqrt(radiiCheck)
  809. Rx *= c
  810. Ry *= c
  811. rxSq = Rx * Rx
  812. rySq = Ry * Ry
  813. }
  814. denom := rxSq*y1PrimeSq + rySq*x1PrimeSq
  815. step2 := 0.0
  816. if a := rxSq*rySq/denom - 1; a > 0 {
  817. step2 = math.Sqrt(a)
  818. }
  819. if largeArc == sweep {
  820. step2 = -step2
  821. }
  822. cxPrime := +step2 * Rx * y1Prime / Ry
  823. cyPrime := -step2 * Ry * x1Prime / Rx
  824. // Step 3: Compute (cx, cy) from (cx′, cy′)
  825. cx := +cosPhi*cxPrime - sinPhi*cyPrime + (x1+x2)/2
  826. cy := +sinPhi*cxPrime + cosPhi*cyPrime + (y1+y2)/2
  827. // Step 4: Compute θ1 and Δθ
  828. ax := (+x1Prime - cxPrime) / Rx
  829. ay := (+y1Prime - cyPrime) / Ry
  830. bx := (-x1Prime - cxPrime) / Rx
  831. by := (-y1Prime - cyPrime) / Ry
  832. theta1 := angle(1, 0, ax, ay)
  833. deltaTheta := angle(ax, ay, bx, by)
  834. if sweep {
  835. if deltaTheta < 0 {
  836. deltaTheta += 2 * math.Pi
  837. }
  838. } else {
  839. if deltaTheta > 0 {
  840. deltaTheta -= 2 * math.Pi
  841. }
  842. }
  843. // This ends the
  844. // https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
  845. // algorithm. What follows below is specific to this implementation.
  846. // We approximate an arc by one or more cubic Bézier curves.
  847. n := int(math.Ceil(math.Abs(deltaTheta) / (math.Pi/2 + 0.001)))
  848. for i := 0; i < n; i++ {
  849. u.arcSegmentTo(cx, cy,
  850. theta1+deltaTheta*float64(i+0)/float64(n),
  851. theta1+deltaTheta*float64(i+1)/float64(n),
  852. Rx, Ry, cosPhi, sinPhi,
  853. )
  854. }
  855. }
  856. // arcSegmentTo approximates an arc by a cubic Bézier curve. The mathematical
  857. // formulae for the control points are the same as that used by librsvg.
  858. func (u *upgrader) arcSegmentTo(cx, cy, theta1, theta2, rx, ry, cosPhi, sinPhi float64) {
  859. halfDeltaTheta := (theta2 - theta1) * 0.5
  860. q := math.Sin(halfDeltaTheta * 0.5)
  861. t := (8 * q * q) / (3 * math.Sin(halfDeltaTheta))
  862. cos1 := math.Cos(theta1)
  863. sin1 := math.Sin(theta1)
  864. cos2 := math.Cos(theta2)
  865. sin2 := math.Sin(theta2)
  866. x1 := rx * (+cos1 - t*sin1)
  867. y1 := ry * (+sin1 + t*cos1)
  868. x2 := rx * (+cos2 + t*sin2)
  869. y2 := ry * (+sin2 - t*cos2)
  870. x3 := rx * (+cos2)
  871. y3 := ry * (+sin2)
  872. highResolutionCoordinates := u.opts.ArcsExpandWithHighResolutionCoordinates
  873. u.verbs = append(u.verbs, upgradeVerbCubeTo)
  874. u.args = append(u.args,
  875. [2]float32{
  876. quantize(float32(cx+cosPhi*x1-sinPhi*y1), highResolutionCoordinates),
  877. quantize(float32(cy+sinPhi*x1+cosPhi*y1), highResolutionCoordinates),
  878. },
  879. [2]float32{
  880. quantize(float32(cx+cosPhi*x2-sinPhi*y2), highResolutionCoordinates),
  881. quantize(float32(cy+sinPhi*x2+cosPhi*y2), highResolutionCoordinates),
  882. },
  883. [2]float32{
  884. quantize(float32(cx+cosPhi*x3-sinPhi*y3), highResolutionCoordinates),
  885. quantize(float32(cy+sinPhi*x3+cosPhi*y3), highResolutionCoordinates),
  886. },
  887. )
  888. }
  889. func countFFV1Instructions(src buffer) (ret uint64) {
  890. for len(src) > 0 {
  891. ret++
  892. opcode := src[0]
  893. src = src[1:]
  894. switch {
  895. case opcode < 0x40:
  896. switch {
  897. case opcode < 0x30:
  898. nReps := uint32(opcode & 15)
  899. if nReps == 0 {
  900. n := 0
  901. nReps, n = src.decodeNaturalFFV1()
  902. src = src[n:]
  903. nReps += 16
  904. }
  905. nCoords := 2 * (1 + int(opcode>>4))
  906. for ; nReps > 0; nReps-- {
  907. for i := 0; i < nCoords; i++ {
  908. _, n := src.decodeNaturalFFV1()
  909. src = src[n:]
  910. }
  911. }
  912. case opcode < 0x35:
  913. for i := 0; i < 4; i++ {
  914. _, n := src.decodeNaturalFFV1()
  915. src = src[n:]
  916. }
  917. case opcode == 0x35:
  918. for i := 0; i < 2; i++ {
  919. _, n := src.decodeNaturalFFV1()
  920. src = src[n:]
  921. }
  922. case opcode == 0x36:
  923. src = src[1:]
  924. case opcode == 0x37:
  925. // No-op.
  926. default:
  927. // upgradeBytecode (with calculatingJumpLOD set) will not emit
  928. // jump or call instructions.
  929. panic("unexpected FFV1 instruction")
  930. }
  931. case opcode < 0x80:
  932. switch (opcode >> 4) & 3 {
  933. case 0, 1:
  934. src = src[4:]
  935. case 2:
  936. src = src[8:]
  937. default:
  938. src = src[8*(2+int(opcode&15)):]
  939. }
  940. case opcode < 0xc0:
  941. switch (opcode >> 4) & 3 {
  942. case 0:
  943. // No-op.
  944. case 1:
  945. src = src[13:]
  946. case 2:
  947. src = src[25:]
  948. default:
  949. // upgradeBytecode (with calculatingJumpLOD set) will not emit
  950. // reserved instructions.
  951. panic("unexpected FFV1 instruction")
  952. }
  953. default:
  954. // upgradeBytecode (with calculatingJumpLOD set) will not emit
  955. // reserved instructions.
  956. panic("unexpected FFV1 instruction")
  957. }
  958. }
  959. return ret
  960. }
  961. type upgradeColor struct {
  962. typ ColorType
  963. paletteIndex uint8
  964. blend uint8
  965. rgba color.RGBA
  966. color0 *upgradeColor
  967. color1 *upgradeColor
  968. }
  969. func (u *upgrader) resolve(c Color, denyBlend bool) (upgradeColor, error) {
  970. switch c.typ {
  971. case ColorTypeRGBA:
  972. return upgradeColor{
  973. typ: ColorTypeRGBA,
  974. rgba: c.data,
  975. }, nil
  976. case ColorTypePaletteIndex:
  977. return upgradeColor{
  978. typ: ColorTypePaletteIndex,
  979. paletteIndex: c.paletteIndex(),
  980. }, nil
  981. case ColorTypeCReg:
  982. upgrade := u.creg[c.cReg()]
  983. if denyBlend && (upgrade.typ == ColorTypeBlend) {
  984. return upgradeColor{}, errUnsupportedUpgrade
  985. }
  986. return upgrade, nil
  987. }
  988. if denyBlend {
  989. return upgradeColor{}, errUnsupportedUpgrade
  990. }
  991. t, c0, c1 := c.blend()
  992. color0, err := u.resolve(decodeColor1(c0), true)
  993. if err != nil {
  994. return upgradeColor{}, err
  995. }
  996. color1, err := u.resolve(decodeColor1(c1), true)
  997. if err != nil {
  998. return upgradeColor{}, err
  999. }
  1000. return upgradeColor{
  1001. typ: ColorTypeBlend,
  1002. blend: t,
  1003. color0: &color0,
  1004. color1: &color1,
  1005. }, nil
  1006. }