// Copyright 2016 The Go Authors. All rights reserved. | |

// Use of this source code is governed by a BSD-style | |

// license that can be found in the LICENSE file. | |

/* | |

Package iconvg implements a compact, binary format for simple vector graphics: | |

icons, logos, glyphs and emoji. | |

WARNING: THIS FORMAT IS EXPERIMENTAL AND SUBJECT TO INCOMPATIBLE CHANGES. | |

It is similar in concept to SVG (Scalable Vector Graphics) but much simpler. | |

Compared to "SVG Tiny", it does not have features for text, multimedia, | |

interactivity, linking, scripting, animation, XSLT, DOM, combination with | |

raster graphics such as JPEG formatted textures, etc. | |

It is a format for efficient presentation, not an authoring format. For | |

example, it does not provide grouping individual paths into higher level | |

objects. Instead, the anticipated workflow is that artists use other tools and | |

authoring formats like Inkscape and SVG, or commercial equivalents, and export | |

IconVG versions of their assets, the same way that they would produce PNG | |

versions of their vector art. It is not a goal to be able to recover the | |

original SVG from a derived IconVG. | |

It is not a pixel-exact format. Different implementations may produce slightly | |

different renderings, due to implementation-specific rounding errors in the | |

mathematical computations when rasterizing vector paths to pixels. Artifacts | |

may appear when scaling up to extreme sizes, say 1 million by 1 million pixels. | |

Nonetheless, at typical scales, e.g. up to 4096 × 4096, such differences are | |

not expected to be perceptible to the naked eye. | |

Structure | |

An IconVG graphic consists of a magic identifier, one or more metadata bytes | |

then a sequence of variable length instructions for a virtual machine. | |

Those instructions encode a sequence of filled paths, similar to SVG path data | |

(https://www.w3.org/TR/SVG/paths.html#PathData). Rendering involves switching | |

between two modes: a styling mode where color registers are set, and a drawing | |

mode where a path's geometry is defined. The virtual machine starts in the | |

styling mode. | |

In both modes, rendering proceeds by reading a one byte opcode followed by a | |

variable number of data bytes for that opcode. The mapping from byte values to | |

opcodes depends on whether the renderer is in the styling or drawing mode. A | |

0x07 byte value means setting the color selector register in the styling mode, | |

and means adding multiple lineto segments in the drawing mode. | |

Level of Detail | |

The machine state includes 2 level of detail registers, denoted LOD0 and LOD1, | |

both float32 values, initialized to +0 and +infinity. Drawing mode opcodes have | |

no effect (other than leaving drawing mode) unless the height H in pixels of | |

the rasterization satisfies LOD0 <= H and H < LOD1. | |

This allows the format to provide a simpler version for small rasterizations | |

(e.g. below 32 pixels) and a more complex version for large rasterizations | |

(e.g. 32 and above pixels). | |

Registers | |

The machine state includes 64 color registers (denoted CREG[0], CREG[1], ..., | |

CREG[63]) and 64 number registers (denoted NREG[0], NREG[1], ..., NREG[63]). | |

Register indexing is done modulo 64, so CREG[70] is the same as CREG[6], and | |

CREG[-1] is the same as CREG[63]. | |

Each CREG and NREG register is 32 bits wide. The CREG registers are initialized | |

to the custom palette (see below); the NREG registers are initialized to 0. The | |

machine state also includes two selector registers, denoted CSEL and NSEL. They | |

are effectively 6 bit integers, as they index CREG and NREG, and are also | |

initialized to 0. | |

Color registers are four uint8 values: red, green, blue and alpha. | |

Number registers are float32 values. | |

Colors and Gradients | |

IconVG graphics work in 32 bit alpha-premultiplied color, with 8 bits for red, | |

green, blue and alpha. Alpha-premultiplication means that c00000c0 represents a | |

75%-opaque, fully saturated red. | |

It also means that some RGBA combinations (where e.g. the red value is greater | |

than the alpha value) are nonsensical. The virtual machine re-purposes some of | |

those values to represent gradients instead of flat colors. Any color register | |

whose alpha value is zero but whose blue value is at least 128 is a gradient. | |

Its remaining bits are reinterpreted such that: | |

The low 6 bits of the red value is the number of color/offset stops, NSTOPS. | |

The high 2 bits of the red value are reserved. | |

The low 6 bits of the green value is the color register base, CBASE. | |

The high 2 bits of the green value is how to spread the gradient past its | |

nominal bounds (from offset being 0.0 to offset being 1.0). The high two bits | |

being 0, 1, 2 or 3 mean none, pad, reflect and repeat respectively. None means | |

that offsets outside of the [0.0, 1.0] range map to transparent black. Pad | |

means that offsets below 0.0 and above 1.0 map to the colors that 0.0 and 1.0 | |

would map to. Reflect means that the offset mapping is reflected start-to-end, | |

end-to-start, start-to-end, etc. Repeat means that the offset mapping is | |

repeated start-to-end, start-to-end, start-to-end, etc. | |

The low 6 bits of the blue value is the number register base, NBASE. | |

The remaining bit (the 0x40 bit) of the blue value denotes the gradient shape: | |

0 means a linear gradient and 1 means a radial gradient. | |

The gradient has NSTOPS color/offset stops. The first stop has color | |

CREG[CBASE+0] and offset NREG[NBASE+0], the second stop has color CREG[CBASE+1] | |

and offset NREG[NBASE+1], and so on. | |

The gradient also uses the six numbers from NREG[NBASE-6] to NREG[NBASE-1], | |

which form an affine transformation matrix [a, b, c; d, e, f] such that | |

a=NREG[NBASE-6], b=NREG[NBASE-5], c=NREG[NBASE-4], etc. This matrix maps from | |

graphic coordinate space (defined by the metadata's viewBox) to gradient | |

coordinate space. Gradient coordinate space is where a linear gradient ranges | |

from x=0 to x=1, and a radial gradient has center (0, 0) and radius 1. | |

The graphic coordinate (px, py) maps to the gradient coordinate (dx, dy) by: | |

dx = a*px + b*py + c | |

dy = d*px + e*py + f | |

The appendix below gives explicit formulae for the [a, b, c; d, e, f] affine | |

transformation matrix for common gradient geometry, such as a linear gradient | |

defined by two points. | |

At the time a gradient is used to fill a path, it is invalid for any of the | |

stop colors to itself be a gradient, or for any stop offset to be less than or | |

equal to a previous offset, or outside the range [0, 1]. | |

Colors | |

Color register values are always 32 bits, or 4 bytes. Colors in the instruction | |

byte stream can be encoded more compactly, and are encoded in either 1, 2, 3 or | |

4 bytes, depending on context. For example, some opcodes are followed by a 1 | |

byte color, others by a 2 byte color. There are two forms of 3 byte colors: | |

direct and indirect. | |

For a 1 byte encoding, byte values in the range [0, 125) encode the RGBA color | |

where the red, green and blue values come from the base-5 encoding of that byte | |

value such that 0, 1, 2, 3 and 4 map to 0x00, 0x40, 0x80, 0xc0, 0xff. The alpha | |

value is 0xff. For example, the color 40ffc0ff can be encoded as 0x30, as | |

decimal 48 equals 1*25 + 4*5 + 3. A byte value of 125, 126 or 127 mean the | |

colors c0c0c0c0, 80808080 and 00000000 respectively. Byte values in the range | |

[128, 192) mean a color from the custom palette (indexed by that byte value | |

minus 128). Byte values in the range [192, 256) mean the value of a CREG color | |

register (with CREG indexed by that byte value minus 192). | |

For a 2 byte encoding, the red, green, blue and alpha values are all 4 bit | |

values. For example, the color 338800ff can be encoded as 0x38 0x0f. | |

For a 3 byte direct encoding, the red, green and blue values are all 8 bit | |

values. The alpha value is implicitly 255. For example, the color 306607ff can | |

be encoded as 0x30 0x66 0x07. | |

For a 4 byte encoding, the red, green, blue and alpha values are all 8 bit | |

values. For example, the color 30660780 is simply 0x30 0x66 0x07 0x80. | |

For a 3 byte indirect encoding, the first byte is an integer value in the range | |

[0, 255] (denoted T) and the second and third bytes are each a 1 byte encoded | |

color (denoted C0 and C1). The resultant color's red channel value is given by: | |

RESULTANT.RED = (((255-T) * C0.RED) + (T * C1.RED) + 128) / 255 | |

rounded down to an integer value (the mathematical floor function), and | |

likewise for the green, blue and alpha channels. For example, if the custom | |

palette's third entry is a fully opaque orange, then 0x40 0x7f 0x82 encodes a | |

25% percent opaque orange: a blend of 75% times fully transparent (1 byte color | |

0x7f) and 25% times a fully opaque orange (1 byte color 0x82). | |

It is valid for some encodings to yield a color value where the red, green or | |

blue value is greater than the alpha value, as this may be a gradient. If it | |

isn't a gradient, the subsequent rendering is undefined. | |

Palettes | |

Rendering an IconVG graphic can be varied by a 64 color palette. For example, | |

an emoji graphic may be rendered with palette color 0 for skin and palette | |

color 1 for hair. Decorative variations, such as different clothing, can be | |

implemented by palette colors possibly set to completely transparent to switch | |

paths off. | |

Rendering software should allow users to pass an optional 64 color palette. If | |

one isn't provided, the suggested palette (either given in the metadata or the | |

default consisting entirely of opaque black) should be used. Whichever palette | |

ends up being used is designated the custom palette. | |

Some user-given colors may be nonsensical as alpha-premultiplied colors, where | |

e.g. the red value is greater than the alpha value. Such colors are replaced by | |

opaque black and not re-interpreted as gradients. | |

Assigning names such as "skin", "hair" or "bow_tie" to the integer indexes of | |

that 64 color palette is out of scope of the IconVG format per se. | |

Numbers | |

Like colors, numbers are encoded in the instruction stream in either 1, 2 or 4 | |

bytes. Unlike colors, the encoding length is not determined by context. | |

Instead, the low two bits of the first byte indicate the encoding length. | |

If the least significant bit (the 0x01 bit) of the first byte is 0, the number | |

is encoded in 1 byte. Otherwise, it is encoded in 2 or 4 bytes depending on the | |

second least significant bit (the 0x02 bit) of the first byte being 0 or 1. | |

Natural Numbers | |

For a 1 byte encoding, the remaining 7 bits form an integer value in the range | |

[0, 1<<7). For example, 0x28 encodes the value 0x14 or, in decimal, 20. | |

For a 2 byte encoding, the remaining 14 bits, interpreted as little endian, | |

form an integer in the range [0, 1<<14). For example, 0x59 0x83 encodes the | |

value 0x20d6 or, in decimal, 8406. | |

For a 4 byte encoding, the remaining 30 bits, interpreted as little endian, | |

form an integer in the range [0, 1<<30). For example, 0x07 0x00 0x80 0x3f | |

encodes the value 0xfe00001 or, in decimal, 266338305. | |

Real Numbers | |

The encoding of a real number resembles the encoding of a natural number. For 1 | |

and 2 byte encodings, the decoded real number equals the decoded natural | |

number. For example, 0x28 encodes the real number 20.0, just as it encodes the | |

natural number 20. | |

For a 4 byte encoding, the decoded natural number, in the range [0, 1<<30), is | |

shifted left by 2, to make a uint32 value that is a multiple of 4. The decoded | |

real number is the floating point number corresponding to the IEEE 754 binary | |

representation of that uint32 (i.e. a reinterpretation as a float32). For | |

example, 0x07 0x00 0x80 0x3f encodes the value 1.000000476837158203125. | |

It is valid for the 4 byte encoding to represent infinities and NaNs, but if | |

not loaded into LOD0 or LOD1, the subsequent rendering is undefined. | |

Coordinate Numbers | |

The encoding of a coordinate number resembles the encoding of a real number. | |

For 1 and 2 byte encodings, the decoded coordinate number equals (R*scale - | |

bias), where R is the decoded real number as above. The scale and bias depends | |

on the number of bytes in the encoding. | |

For a 1 byte encoding, the scale is 1 and the bias is 64, so that a 1 byte | |

coordinate ranges in [-64, +64) at integer granularity. For example, the | |

coordinate 7 can be encoded as 0x8e. | |

For a 2 byte encoding, the scale is 1/64 and the bias is 128, so that a 2 byte | |

coordinate ranges in [-128, +128) at 1/64 granularity. For example, the | |

coordinate 7.5 can be encoded as 0x81 0x87. | |

For a 4 byte encoding, the decoded coordinate number simply equals R. For | |

example, the coordinate 7.5 can also be encoded as 0x03 0x00 0xf0 0x40. | |

Zero-to-One Numbers | |

A zero-to-one number is a real number that is typically in the range [0, 1], | |

although it is valid for a value to be outside of that range. For example, | |

angles are expressed as a zero-to-one number: a fraction of a complete | |

revolution (360 degrees). Gradient stop offsets are another example. | |

The encoding of a zero-to-one number resembles the encoding of a real number. | |

For 1 and 2 byte encodings, the decoded number equals R*scale, where R is the | |

decoded real number as above. The scale depends on the number of bytes in the | |

encoding. | |

For a 1 byte encoding, the real number (ranging up to 128) is scaled by 1/120. | |

The denominator is 2*2*2 * 3 * 5, so that 15 degrees (2*π/24 radians) can be | |

encoded as 0x0a. | |

For a 2 byte encoding, the real number (ranging up to 16384) is scaled by | |

1/15120. The denominator is 2*2*2*2 * 3*3*3 * 5 * 7. For example, 40 degrees | |

(2*π/9 radians) can be encoded as 0x41 0x1a. | |

For a 4 byte encoding, the decoded zero-to-one number simply equals R. For | |

example, 1 degree (2*π/360 radians), or 0.002777777..., can be approximated by | |

the encoding 0x63 0x0b 0x36 0x3b. | |

Magic Identifier | |

An IconVG graphic starts with the four bytes 0x89 0x49 0x56 0x47 ("\x89IVG"). | |

Metadata | |

The encoded metadata starts with a natural number (see encoding above) of the | |

number of metadata chunks in the metadata, followed by that many chunks. Each | |

chunk starts with the length remaining in the chunk (again, encoded as a | |

natural number), not including the chunk length itself. After that is a MID | |

(Metadata Identifier) natural number, then MID-specific data. Chunks must be | |

presented in increasing MID order. MIDs cannot be repeated. All MIDs are | |

optional. | |

MID 0 - ViewBox | |

Metadata Identifier 0 means that the MID-specific data contains four coordinate | |

values (see above for the coordinate encoding). These are the minX, minY, maxX, | |

maxY of the graphic's viewBox: its bounding rectangle in (scalable) vector | |

space. Note that these are abstract units, and not necessarily 1:1 with pixels. | |

If this MID is not present, the viewBox defaults to (-32, -32, +32, +32). A | |

viewBox is invalid if minX > maxX or if minY > maxY or if at least one of those | |

four values are a NaN or an infinity. | |

MID 1 - Suggested Palette | |

Metadata Identifier 1 means that the MID-specific data contains a suggested | |

palette, e.g. to provide a default rendering of variable colors such as an | |

emoji's skin and hair. The suggested palette is encoded in at least one byte. | |

The low 6 bits of that byte form a number N. The high 2 bits denote the palette | |

color format: those high 2 bits being 0, 1, 2 or 3 mean 1, 2, 3 (direct) or 4 | |

byte colors (see above for the color encoding). The chunk then contains N+1 | |

explicit colors, in that 1, 2, 3 or 4 byte encoding. A palette has exactly 64 | |

colors, the 63-N implicit colors of the suggested palette are set to opaque | |

black. A 1 byte color that refers to the custom palette or a CREG color | |

register resolves to opaque black. If this MID is not present, the suggested | |

palette consists entirely of opaque black, as black is always fashionable. | |

Styling Opcodes | |

Some opcode descriptions refer to an adjustment value, ADJ. That value is the | |

low three bits of the opcode, nominally in the range [0, 8), although in | |

practice the range is [0, 7) as no ADJ-using opcode has the low three bits set. | |

Opcodes 0x00 to 0x3f sets CSEL to the low 6 bits of the opcode. | |

Opcodes 0x40 to 0x7f sets NSEL to the low 6 bits of the opcode. | |

Opcodes 0x80 to 0x86 sets CREG[CSEL-ADJ] to the 1 byte encoded color following | |

the opcode. | |

Opcodes 0x88 to 0x8e sets CREG[CSEL-ADJ] to the 2 byte encoded color following | |

the opcode. | |

Opcodes 0x90 to 0x96 sets CREG[CSEL-ADJ] to the 3 byte direct encoded color | |

following the opcode. | |

Opcodes 0x98 to 0x9e sets CREG[CSEL-ADJ] to the 4 byte encoded color following | |

the opcode. | |

Opcodes 0xa0 to 0xa6 sets CREG[CSEL-ADJ] to the 3 byte indirect encoded color | |

following the opcode. | |

Opcodes 0x87, 0x8f, 0x97, 0x9f and 0xa7 sets CREG[CSEL] to the 1, 2, 3 | |

(direct), 4 and 3 (indirect) byte encoded color, following the opcode, and then | |

increments CSEL by 1. | |

Opcodes 0xa8 to 0xae sets NREG[NSEL-ADJ] to the real number following the | |

opcode. | |

Opcodes 0xb0 to 0xb6 sets NREG[NSEL-ADJ] to the coordinate number following the | |

opcode. | |

Opcodes 0xb8 to 0xbe sets NREG[NSEL-ADJ] to the zero-to-one number following | |

the opcode. | |

Opcode 0xaf, 0xb7 and 0xbf sets NREG[NSEL] to the real, coordinate and | |

zero-to-one number following the opcode, and then increments NSEL by 1. | |

Opcodes 0xc0 to 0xc6 switches to the drawing mode, and is followed by two | |

coordinates that is the path's starting location. In effect, there is an | |

implicit M (absolute moveto) op. CREG[CSEL-ADJ], either a flat color or a | |

gradient, will fill the path once it is complete. | |

Opcode 0xc7 sets the Level of Detail bounds LOD0 and LOD1 to the two real | |

numbers following the opcode. | |

All other opcodes are reserved. | |

Drawing Opcodes | |

The drawing model is based on SVG path data | |

(https://www.w3.org/TR/SVG/paths.html#PathData) and this description re-uses | |

SVG's one-letter mnemonics: M means absolute moveto, m means relative moveto, L | |

means absolute lineto, l means relative lineto, H means absolute horizontal | |

lineto, etc. Upper and lower case mean absolute and relative coordinates. The | |

upper case mnemonics of the SVG operations used in IconVG's drawing mode are: | |

M, Z, L, H, V, C, S, Q, T, A. | |

IconVG differs from SVG with multiple consecutive moveto ops. SVG treats all | |

but the first one as lineto ops. IconVG treats them all as moveto ops. | |

Almost all opcodes, i.e. those in the range [0x00, 0xdf], come in contiguous | |

groups of 16 or 32. For example, there are 16 Q (absolute quadratic Bézier | |

curveto) opcodes, from 0x60 to 0x6f. Those opcodes' meaning differ only in | |

their repeat count RC: how often that drawing operation is repeated. The lowest | |

valued opcode has a repeat count of 1, the next lowest has a repeat count of 2 | |

and so on. For example, the opcode 0x68 means 9 consecutive Q drawing ops. | |

Opcodes 0x00 to 0x1f means RC consecutive L ops, for RC in [1, 32]. The opcode | |

is followed by 2*RC coordinates, RC sets of (x, y). | |

Opcodes 0x20 to 0x3f are like the previous paragraph, except L (absolute) | |

becomes l (relative). | |

Opcodes 0x40 to 0x4f means RC consecutive T ops, for RC in [1, 16]. The opcode | |

is followed by 2*RC coordinates, RC sets of (x, y). | |

Opcodes 0x50 to 0x5f are like the previous paragraph, except T (absolute) | |

becomes t (relative). | |

Opcodes 0x60 to 0x6f means RC consecutive Q ops, for RC in [1, 16]. The opcode | |

is followed by 4*RC coordinates, RC sets of (x1, y1, x, y). | |

Opcodes 0x70 to 0x7f are like the previous paragraph, except Q (absolute) | |

becomes q (relative). | |

Opcodes 0x80 to 0x8f means RC consecutive S ops, for RC in [1, 16]. The opcode | |

is followed by 4*RC coordinates, RC sets of (x2, y2, x, y). | |

Opcodes 0x90 to 0x9f are like the previous paragraph, except S (absolute) | |

becomes s (relative). | |

Opcodes 0xa0 to 0xaf means RC consecutive C ops, for RC in [1, 16]. The opcode | |

is followed by 6*RC coordinates, RC sets of (x1, y1, x2, y2, x, y). | |

Opcodes 0xb0 to 0xbf are like the previous paragraph, except C (absolute) | |

becomes c (relative). | |

Opcodes 0xc0 to 0xcf means RC consecutive A ops, for RC in [1, 16]. The opcode | |

is followed by 6*RC numbers, RC sets of (rx, ry, xAxisRotation, flags, x, y). | |

The rx, ry, x and y numbers are coordinates. The xAxisRotation number is an | |

angle (a zero-to-one number being a fraction of 360 degrees). The flags are | |

encoded as a natural number. The 0x01 bit of the decoded natural number is the | |

large-arc-flag and the 0x02 bit is the sweep-flag. | |

Opcodes 0xd0 to 0xdf are like the previous paragraph, except A (absolute) | |

becomes a (relative). | |

Opcode 0xe0 is reserved. (A future version of IconVG may use this opcode to | |

mean the same as 0xe1 without the one z op, which will matter for stroked | |

paths). | |

Opcode 0xe1 means one z op and then end the path: fill the path with the color | |

chosen when we switched to the drawing mode, and switch back to the styling | |

mode. (The Z and z ops are equivalent). | |

Opcode 0xe2 means one z op and then an implicit M op to the (x, y) coordinates | |

after the opcode. | |

Opcode 0xe3 means one z op and then an implicit m op to the (x, y) coordinates | |

after the opcode. | |

Opcodes 0xe4 and 0xe5 are reserved. (A future version of IconVG may use these | |

for M and m ops, if we allow stroked paths). | |

Opcode 0xe6 means one H op to the x coordinate after the opcode. | |

Opcode 0xe7 means one h op to the x coordinate after the opcode. | |

Opcode 0xe8 means one V op to the y coordinate after the opcode. | |

Opcode 0xe9 means one v op to the y coordinate after the opcode. | |

All other opcodes are reserved. | |

These opcode descriptions all assume that the Level of Detail constraint (see | |

above) is satisfied. If not, the opcodes and their variable length data are | |

consumed, but no further action is taken (other than leaving drawing mode). | |

Example | |

The production version of the "action/info" icon from the Material Design icon | |

set is defined by the following SVG, also available at | |

https://github.com/google/material-design-icons/blob/master/action/svg/production/ic_info_48px.svg: | |

<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"> | |

<path d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4z | |

m2 30h-4V22h4v12zm0-16h-4v-4h4v4z"/></svg> | |

This is 202 bytes, or 174 bytes after "gzip --best". The PNG renderings at | |

various sizes: | |

18x18: 207 bytes | |

24x24: 222 bytes | |

36x36: 321 bytes | |

48x48: 412 bytes | |

The corresponding IconVG is 73 bytes: | |

89 49 56 47 02 0a 00 50 50 b0 b0 c0 80 58 a0 cf | |

cc 30 c1 58 58 cf cc 30 c1 58 80 91 37 33 0f 41 | |

a8 a8 a8 a8 37 33 0f c1 a8 58 80 cf cc 30 41 58 | |

80 58 e3 84 bc e7 78 e8 7c e7 88 e9 98 e3 80 60 | |

e7 78 e9 78 e7 88 e9 88 e1 | |

The annotated version is below. Note that the IconVG viewBox ranges from -24 to | |

+24 while the SVG viewBox ranges from 0 to 48. | |

89 49 56 47 IconVG Magic identifier | |

02 Number of metadata chunks: 1 | |

0a Metadata chunk length: 5 | |

00 Metadata Identifier: 0 (viewBox) | |

50 -24 | |

50 -24 | |

b0 +24 | |

b0 +24 | |

c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) | |

80 +0 | |

58 -20 | |

a0 C (absolute cubeTo), 1 reps | |

cf cc 30 c1 -11.049999 | |

58 -20 | |

58 -20 | |

cf cc 30 c1 -11.049999 | |

58 -20 | |

80 +0 | |

91 s (relative smooth cubeTo), 2 reps | |

37 33 0f 41 +8.950001 | |

a8 +20 | |

a8 +20 | |

a8 +20 | |

s (relative smooth cubeTo), implicit | |

a8 +20 | |

37 33 0f c1 -8.950001 | |

a8 +20 | |

58 -20 | |

80 S (absolute smooth cubeTo), 1 reps | |

cf cc 30 41 +11.049999 | |

58 -20 | |

80 +0 | |

58 -20 | |

e3 z (closePath); m (relative moveTo) | |

84 +2 | |

bc +30 | |

e7 h (relative horizontal lineTo) | |

78 -4 | |

e8 V (absolute vertical lineTo) | |

7c -2 | |

e7 h (relative horizontal lineTo) | |

88 +4 | |

e9 v (relative vertical lineTo) | |

98 +12 | |

e3 z (closePath); m (relative moveTo) | |

80 +0 | |

60 -16 | |

e7 h (relative horizontal lineTo) | |

78 -4 | |

e9 v (relative vertical lineTo) | |

78 -4 | |

e7 h (relative horizontal lineTo) | |

88 +4 | |

e9 v (relative vertical lineTo) | |

88 +4 | |

e1 z (closePath); end path | |

There are more examples in the ./testdata directory. | |

Appendix - Gradient Transformation Matrices | |

This appendix derives the affine transformation matrices [a, b, c; d, e, f] for | |

linear, circular and elliptical gradients. | |

Linear Gradients | |

For a linear gradient from (x1, y1) to (x2, y2), let dx, dy = x2-x1, y2-y1. In | |

gradient coordinate space, the y-coordinate is ignored, so the transformation | |

matrix simplifies to [a, b, c; 0, 0, 0]. It satisfies the three simultaneous | |

equations: | |

a*(x1 ) + b*(y1 ) + c = 0 (eq L.0) | |

a*(x1+dy) + b*(y1-dx) + c = 0 (eq L.1) | |

a*(x1+dx) + b*(y1+dy) + c = 1 (eq L.2) | |

Subtracting equation L.0 from equations L.1 and L.2 yields: | |

a*dy - b*dx = 0 | |

a*dx + b*dy = 1 | |

So that | |

a*dy*dy - b*dx*dy = 0 | |

a*dx*dx + b*dx*dy = dx | |

Overall: | |

a = dx / (dx*dx + dy*dy) | |

b = dy / (dx*dx + dy*dy) | |

c = -a*x1 - b*y1 | |

d = 0 | |

e = 0 | |

f = 0 | |

Circular Gradients | |

For a circular gradient with center (cx, cy) and radius vector (rx, ry), such | |

that (cx+rx, cy+ry) is on the circle, let | |

r = math.Sqrt(rx*rx + ry*ry) | |

The transformation matrix maps (cx, cy) to (0, 0), maps (cx+r, cy) to (1, 0) | |

and maps (cx, cy+r) to (0, 1). Solving those six simultaneous equations give: | |

a = +1 / r | |

b = +0 / r | |

c = -cx / r | |

d = +0 / r | |

e = +1 / r | |

f = -cy / r | |

Elliptical Gradients | |

For an elliptical gradient with center (cx, cy) and axis vectors (rx, ry) and | |

(sx, sy), such that (cx+rx, cx+ry) and (cx+sx, cx+sy) are on the ellipse, the | |

transformation matrix satisfies the six simultaneous equations: | |

a*(cx ) + b*(cy ) + c = 0 (eq E.0) | |

a*(cx+rx) + b*(cy+ry) + c = 1 (eq E.1) | |

a*(cx+sx) + b*(cy+sy) + c = 0 (eq E.2) | |

d*(cx ) + e*(cy ) + f = 0 (eq E.3) | |

d*(cx+rx) + e*(cy+ry) + f = 0 (eq E.4) | |

d*(cx+sx) + e*(cy+sy) + f = 1 (eq E.5) | |

Subtracting equation E.0 from equations E.1 and E.2 yields: | |

a*rx + b*ry = 1 | |

a*sx + b*sy = 0 | |

Solving these two simultaneous equations yields: | |

a = +sy / (rx*sy - sx*ry) | |

b = -sx / (rx*sy - sx*ry) | |

Re-arranging E.0 yields: | |

c = -a*cx - b*cy | |

Similarly for d, e and f so that, overall: | |

a = +sy / (rx*sy - sx*ry) | |

b = -sx / (rx*sy - sx*ry) | |

c = -a*cx - b*cy | |

d = -ry / (rx*sy - sx*ry) | |

e = +rx / (rx*sy - sx*ry) | |

f = -d*cx - e*cy | |

Note that if rx = r, ry = 0, sx = 0 and sy = r then this simplifies to the | |

circular gradient transformation matrix formula, above. | |

*/ | |

package iconvg | |

// TODO: shapes (circles, rects) and strokes? Or can we assume that authoring | |

// tools will convert shapes and strokes to paths? | |

// TODO: mark somehow that a graphic (such as a back arrow) should be flipped | |

// horizontally or its paths otherwise varied when presented in a Right-To-Left | |

// context, such as among Arabic and Hebrew text? Or should that be the | |

// responsibility of higher layers, selecting different IconVG graphics based | |

// on context, the way they would select different PNG graphics. | |

// TODO: hinting? |