chore(v2): vendor dependencies for offline/China builds

go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor
so go run/build work without hitting proxy.golang.org (blocked/slow in
China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and
GOOS=windows go build -mod=vendor ./internal/ui both pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ila
2026-07-23 16:35:01 +08:00
co-authored by Claude Opus 4.8
parent 97c1c4a974
commit f58728cddd
972 changed files with 597802 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+343
View File
@@ -0,0 +1,343 @@
// 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
import (
"image/color"
"math"
)
// buffer holds an encoded IconVG graphic.
//
// The decodeXxx methods return the decoded value and an integer n, the number
// of bytes that value was encoded in. They return n == 0 if an error occurred.
//
// The encodeXxx methods append to the buffer, modifying the slice in place.
type buffer []byte
func (b buffer) decodeNatural() (u uint32, n int) {
if len(b) < 1 {
return 0, 0
}
x := b[0]
if x&0x01 == 0 {
return uint32(x) >> 1, 1
}
if x&0x02 == 0 {
if len(b) >= 2 {
y := uint16(b[0]) | uint16(b[1])<<8
return uint32(y) >> 2, 2
}
return 0, 0
}
if len(b) >= 4 {
y := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
return y >> 2, 4
}
return 0, 0
}
// decodeNaturalFFV1 is like decodeNatural but for File Format Version 1. See
// https://github.com/google/iconvg/issues/33
func (b buffer) decodeNaturalFFV1() (u uint32, n int) {
if len(b) < 1 {
return 0, 0
}
x := b[0]
if x&0x01 != 0 {
return uint32(x) >> 1, 1
}
if x&0x02 != 0 {
if len(b) >= 2 {
y := uint16(b[0]) | uint16(b[1])<<8
return uint32(y) >> 2, 2
}
return 0, 0
}
if len(b) >= 4 {
y := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
return y >> 2, 4
}
return 0, 0
}
func (b buffer) decodeReal() (f float32, n int) {
switch u, n := b.decodeNatural(); n {
case 0:
return 0, n
case 1:
return float32(u), n
case 2:
return float32(u), n
default:
return math.Float32frombits(u << 2), n
}
}
func (b buffer) decodeCoordinate() (f float32, n int) {
switch u, n := b.decodeNatural(); n {
case 0:
return 0, n
case 1:
return float32(int32(u) - 64), n
case 2:
return float32(int32(u)-64*128) / 64, n
default:
return math.Float32frombits(u << 2), n
}
}
func (b buffer) decodeZeroToOne() (f float32, n int) {
switch u, n := b.decodeNatural(); n {
case 0:
return 0, n
case 1:
return float32(u) / 120, n
case 2:
return float32(u) / 15120, n
default:
return math.Float32frombits(u << 2), n
}
}
func (b buffer) decodeColor1() (c Color, n int) {
if len(b) < 1 {
return Color{}, 0
}
return decodeColor1(b[0]), 1
}
func (b buffer) decodeColor2() (c Color, n int) {
if len(b) < 2 {
return Color{}, 0
}
return RGBAColor(color.RGBA{
R: 0x11 * (b[0] >> 4),
G: 0x11 * (b[0] & 0x0f),
B: 0x11 * (b[1] >> 4),
A: 0x11 * (b[1] & 0x0f),
}), 2
}
func (b buffer) decodeColor3Direct() (c Color, n int) {
if len(b) < 3 {
return Color{}, 0
}
return RGBAColor(color.RGBA{
R: b[0],
G: b[1],
B: b[2],
A: 0xff,
}), 3
}
func (b buffer) decodeColor4() (c Color, n int) {
if len(b) < 4 {
return Color{}, 0
}
return RGBAColor(color.RGBA{
R: b[0],
G: b[1],
B: b[2],
A: b[3],
}), 4
}
func (b buffer) decodeColor3Indirect() (c Color, n int) {
if len(b) < 3 {
return Color{}, 0
}
return BlendColor(b[0], b[1], b[2]), 3
}
func (b *buffer) encodeNatural(u uint32) {
if u < 1<<7 {
u = (u << 1)
*b = append(*b, uint8(u))
return
}
if u < 1<<14 {
u = (u << 2) | 1
*b = append(*b, uint8(u), uint8(u>>8))
return
}
u = (u << 2) | 3
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
}
// encodeNaturalFFV1 is like encodeNatural but for File Format Version 1. See
// https://github.com/google/iconvg/issues/33
func (b *buffer) encodeNaturalFFV1(u uint32) {
if u < 1<<7 {
u = (u << 1) | 0x01
*b = append(*b, uint8(u))
return
}
if u < 1<<14 {
u = (u << 2) | 0x02
*b = append(*b, uint8(u), uint8(u>>8))
return
}
u = (u << 2)
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
}
func (b *buffer) encodeReal(f float32) int {
if u := uint32(f); float32(u) == f && u < 1<<14 {
if u < 1<<7 {
u = (u << 1)
*b = append(*b, uint8(u))
return 1
}
u = (u << 2) | 1
*b = append(*b, uint8(u), uint8(u>>8))
return 2
}
b.encode4ByteReal(f)
return 4
}
func (b *buffer) encode4ByteReal(f float32) {
u := math.Float32bits(f)
// Round the fractional bits (the low 23 bits) to the nearest multiple of
// 4, being careful not to overflow into the upper bits.
v := u & 0x007fffff
if v < 0x007ffffe {
v += 2
}
u = (u & 0xff800000) | v
// A 4 byte encoding has the low two bits set.
u |= 0x03
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
}
// encode4ByteRealFFV1 is like encode4ByteReal but for File Format Version 1.
// See https://github.com/google/iconvg/issues/33
func (b *buffer) encode4ByteRealFFV1(f float32) {
u := math.Float32bits(f)
// Round the fractional bits (the low 23 bits) to the nearest multiple of
// 4, being careful not to overflow into the upper bits.
v := u & 0x007fffff
if v < 0x007ffffe {
v += 2
}
u = (u & 0xff800000) | v
// A 4 byte encoding has the low two bits unset.
u &= 0xfffffffc
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
}
func (b *buffer) encodeCoordinate(f float32) int {
if i := int32(f); -64 <= i && i < +64 && float32(i) == f {
u := uint32(i + 64)
u = (u << 1)
*b = append(*b, uint8(u))
return 1
}
if i := int32(f * 64); -128*64 <= i && i < +128*64 && float32(i) == f*64 {
u := uint32(i + 128*64)
u = (u << 2) | 1
*b = append(*b, uint8(u), uint8(u>>8))
return 2
}
b.encode4ByteReal(f)
return 4
}
// encodeCoordinateFFV1 is like encodeCoordinate but for File Format Version 1.
// See https://github.com/google/iconvg/issues/33
func (b *buffer) encodeCoordinateFFV1(f float32) int {
if i := int32(f); -64 <= i && i < +64 && float32(i) == f {
u := uint32(i + 64)
u = (u << 1) | 0x01
*b = append(*b, uint8(u))
return 1
}
if i := int32(f * 64); -128*64 <= i && i < +128*64 && float32(i) == f*64 {
u := uint32(i + 128*64)
u = (u << 2) | 0x02
*b = append(*b, uint8(u), uint8(u>>8))
return 2
}
b.encode4ByteRealFFV1(f)
return 4
}
func (b *buffer) encodeCoordinatePairFFV1(f [2]float32) int {
n0 := b.encodeCoordinateFFV1(f[0])
n1 := b.encodeCoordinateFFV1(f[1])
return n0 + n1
}
func (b *buffer) encodeAngle(f float32) int {
// Normalize f to the range [0, 1).
g := float64(f)
g -= math.Floor(g)
return b.encodeZeroToOne(float32(g))
}
func (b *buffer) encodeZeroToOne(f float32) int {
if u := uint32(f * 15120); float32(u) == f*15120 && u < 15120 {
if u%126 == 0 {
u = ((u / 126) << 1)
*b = append(*b, uint8(u))
return 1
}
u = (u << 2) | 1
*b = append(*b, uint8(u), uint8(u>>8))
return 2
}
b.encode4ByteReal(f)
return 4
}
func (b *buffer) encodeColor1(c Color) {
if x, ok := encodeColor1(c); ok {
*b = append(*b, x)
return
}
// Default to opaque black.
*b = append(*b, 0x00)
}
func (b *buffer) encodeColor2(c Color) {
if x, ok := encodeColor2(c); ok {
*b = append(*b, x[0], x[1])
return
}
// Default to opaque black.
*b = append(*b, 0x00, 0x0f)
}
func (b *buffer) encodeColor3Direct(c Color) {
if x, ok := encodeColor3Direct(c); ok {
*b = append(*b, x[0], x[1], x[2])
return
}
// Default to opaque black.
*b = append(*b, 0x00, 0x00, 0x00)
}
func (b *buffer) encodeColor4(c Color) {
if x, ok := encodeColor4(c); ok {
*b = append(*b, x[0], x[1], x[2], x[3])
return
}
// Default to opaque black.
*b = append(*b, 0x00, 0x00, 0x00, 0xff)
}
func (b *buffer) encodeColor3Indirect(c Color) {
if x, ok := encodeColor3Indirect(c); ok {
*b = append(*b, x[0], x[1], x[2])
return
}
// Default to opaque black.
*b = append(*b, 0x00, 0x00, 0x00)
}
+180
View File
@@ -0,0 +1,180 @@
// 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
import (
"image/color"
)
func validAlphaPremulColor(c color.RGBA) bool {
return c.R <= c.A && c.G <= c.A && c.B <= c.A
}
// ColorType distinguishes types of Colors.
type ColorType uint8
const (
// ColorTypeRGBA is a direct RGBA color.
ColorTypeRGBA ColorType = iota
// ColorTypePaletteIndex is an indirect color, indexing the custom palette.
ColorTypePaletteIndex
// ColorTypeCReg is an indirect color, indexing the CREG color registers.
ColorTypeCReg
// ColorTypeBlend is an indirect color, blending two other colors.
ColorTypeBlend
)
// Color is an IconVG color, whose RGBA values can depend on context. Some
// Colors are direct RGBA values. Other Colors are indirect, referring to an
// index of the custom palette, a color register of the decoder virtual
// machine, or a blend of two other Colors.
//
// See the "Colors" section in the package documentation for details.
type Color struct {
typ ColorType
data color.RGBA
}
func (c Color) rgba() color.RGBA { return c.data }
func (c Color) paletteIndex() uint8 { return c.data.R }
func (c Color) cReg() uint8 { return c.data.R }
func (c Color) blend() (t, c0, c1 uint8) { return c.data.R, c.data.G, c.data.B }
// Resolve resolves the Color's RGBA value, given its context: the custom
// palette and the color registers of the decoder virtual machine.
func (c Color) Resolve(pal *Palette, cReg *[64]color.RGBA) color.RGBA {
switch c.typ {
case ColorTypeRGBA:
return c.rgba()
case ColorTypePaletteIndex:
return pal[c.paletteIndex()&0x3f]
case ColorTypeCReg:
return cReg[c.cReg()&0x3f]
}
t, c0, c1 := c.blend()
p, q := uint32(255-t), uint32(t)
rgba0 := decodeColor1(c0).Resolve(pal, cReg)
rgba1 := decodeColor1(c1).Resolve(pal, cReg)
return color.RGBA{
uint8(((p * uint32(rgba0.R)) + q*uint32(rgba1.R) + 128) / 255),
uint8(((p * uint32(rgba0.G)) + q*uint32(rgba1.G) + 128) / 255),
uint8(((p * uint32(rgba0.B)) + q*uint32(rgba1.B) + 128) / 255),
uint8(((p * uint32(rgba0.A)) + q*uint32(rgba1.A) + 128) / 255),
}
}
// RGBAColor returns a direct Color.
func RGBAColor(c color.RGBA) Color { return Color{ColorTypeRGBA, c} }
// PaletteIndexColor returns an indirect Color referring to an index of the
// custom palette.
func PaletteIndexColor(i uint8) Color { return Color{ColorTypePaletteIndex, color.RGBA{R: i & 0x3f}} }
// CRegColor returns an indirect Color referring to a color register of the
// decoder virtual machine.
func CRegColor(i uint8) Color { return Color{ColorTypeCReg, color.RGBA{R: i & 0x3f}} }
// BlendColor returns an indirect Color that blends two other Colors. Those two
// other Colors must both be encodable as a 1 byte color.
//
// To blend a Color that is not encodable as a 1 byte color, first load that
// Color into a CREG color register, then call CRegColor to produce a Color
// that is encodable as a 1 byte color. See testdata/favicon.ivg for an
// example.
//
// See the "Colors" section in the package documentation for details.
func BlendColor(t, c0, c1 uint8) Color { return Color{ColorTypeBlend, color.RGBA{R: t, G: c0, B: c1}} }
func decodeColor1(x byte) Color {
if x >= 0x80 {
if x >= 0xc0 {
return CRegColor(x)
} else {
return PaletteIndexColor(x)
}
}
if x >= 125 {
switch x - 125 {
case 0:
return RGBAColor(color.RGBA{0xc0, 0xc0, 0xc0, 0xc0})
case 1:
return RGBAColor(color.RGBA{0x80, 0x80, 0x80, 0x80})
case 2:
return RGBAColor(color.RGBA{0x00, 0x00, 0x00, 0x00})
}
}
blue := dc1Table[x%5]
x = x / 5
green := dc1Table[x%5]
x = x / 5
red := dc1Table[x]
return RGBAColor(color.RGBA{red, green, blue, 0xff})
}
var dc1Table = [5]byte{0x00, 0x40, 0x80, 0xc0, 0xff}
func is1(u uint8) bool { return u&0x3f == 0 || u == 0xff }
func encodeColor1(c Color) (x byte, ok bool) {
switch c.typ {
case ColorTypeRGBA:
if c.data.A != 0xff {
switch c.data {
case color.RGBA{0x00, 0x00, 0x00, 0x00}:
return 127, true
case color.RGBA{0x80, 0x80, 0x80, 0x80}:
return 126, true
case color.RGBA{0xc0, 0xc0, 0xc0, 0xc0}:
return 125, true
}
} else if is1(c.data.R) && is1(c.data.G) && is1(c.data.B) && is1(c.data.A) {
r := c.data.R / 0x3f
g := c.data.G / 0x3f
b := c.data.B / 0x3f
return 25*r + 5*g + b, true
}
case ColorTypePaletteIndex:
return c.data.R | 0x80, true
case ColorTypeCReg:
return c.data.R | 0xc0, true
}
return 0, false
}
func is2(u uint8) bool { return u%0x11 == 0 }
func encodeColor2(c Color) (x [2]byte, ok bool) {
if c.typ == ColorTypeRGBA && is2(c.data.R) && is2(c.data.G) && is2(c.data.B) && is2(c.data.A) {
return [2]byte{
(c.data.R/0x11)<<4 | (c.data.G / 0x11),
(c.data.B/0x11)<<4 | (c.data.A / 0x11),
}, true
}
return [2]byte{}, false
}
func encodeColor3Direct(c Color) (x [3]byte, ok bool) {
if c.typ == ColorTypeRGBA && c.data.A == 0xff {
return [3]byte{c.data.R, c.data.G, c.data.B}, true
}
return [3]byte{}, false
}
func encodeColor4(c Color) (x [4]byte, ok bool) {
if c.typ == ColorTypeRGBA {
return [4]byte{c.data.R, c.data.G, c.data.B, c.data.A}, true
}
return [4]byte{}, false
}
func encodeColor3Indirect(c Color) (x [3]byte, ok bool) {
if c.typ == ColorTypeBlend {
return [3]byte{c.data.R, c.data.G, c.data.B}, true
}
return [3]byte{}, false
}
+699
View File
@@ -0,0 +1,699 @@
// 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
import (
"bytes"
"errors"
"image/color"
)
var (
errInconsistentMetadataChunkLength = errors.New("iconvg: inconsistent metadata chunk length")
errInvalidColor = errors.New("iconvg: invalid color")
errInvalidMagicIdentifier = errors.New("iconvg: invalid magic identifier")
errInvalidMetadataChunkLength = errors.New("iconvg: invalid metadata chunk length")
errInvalidMetadataIdentifier = errors.New("iconvg: invalid metadata identifier")
errInvalidNumber = errors.New("iconvg: invalid number")
errInvalidNumberOfMetadataChunks = errors.New("iconvg: invalid number of metadata chunks")
errInvalidSuggestedPalette = errors.New("iconvg: invalid suggested palette")
errInvalidViewBox = errors.New("iconvg: invalid view box")
errUnsupportedDrawingOpcode = errors.New("iconvg: unsupported drawing opcode")
errUnsupportedMetadataIdentifier = errors.New("iconvg: unsupported metadata identifier")
errUnsupportedStylingOpcode = errors.New("iconvg: unsupported styling opcode")
errUnsupportedUpgrade = errors.New("iconvg: unsupported upgrade")
)
var midDescriptions = [...]string{
midViewBox: "viewBox",
midSuggestedPalette: "suggested palette",
}
// Destination handles the actions decoded from an IconVG graphic's opcodes.
//
// When passed to Decode, the first method called (if any) will be Reset. No
// methods will be called at all if an error is encountered in the encoded form
// before the metadata is fully decoded.
type Destination interface {
Reset(m Metadata)
SetCSel(cSel uint8)
SetNSel(nSel uint8)
SetCReg(adj uint8, incr bool, c Color)
SetNReg(adj uint8, incr bool, f float32)
SetLOD(lod0, lod1 float32)
StartPath(adj uint8, x, y float32)
ClosePathEndPath()
ClosePathAbsMoveTo(x, y float32)
ClosePathRelMoveTo(x, y float32)
AbsHLineTo(x float32)
RelHLineTo(x float32)
AbsVLineTo(y float32)
RelVLineTo(y float32)
AbsLineTo(x, y float32)
RelLineTo(x, y float32)
AbsSmoothQuadTo(x, y float32)
RelSmoothQuadTo(x, y float32)
AbsQuadTo(x1, y1, x, y float32)
RelQuadTo(x1, y1, x, y float32)
AbsSmoothCubeTo(x2, y2, x, y float32)
RelSmoothCubeTo(x2, y2, x, y float32)
AbsCubeTo(x1, y1, x2, y2, x, y float32)
RelCubeTo(x1, y1, x2, y2, x, y float32)
AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32)
RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32)
}
type printer func(b []byte, format string, args ...interface{})
// DecodeOptions are the optional parameters to the Decode function.
type DecodeOptions struct {
// Palette is an optional 64 color palette. If one isn't provided, the
// IconVG graphic's suggested palette will be used.
Palette *Palette
}
// DecodeMetadata decodes only the metadata in an IconVG graphic.
func DecodeMetadata(src []byte) (m Metadata, err error) {
m.ViewBox = DefaultViewBox
m.Palette = DefaultPalette
if err = decode(nil, nil, &m, true, src, nil); err != nil {
return Metadata{}, err
}
return m, nil
}
// Decode decodes an IconVG graphic.
func Decode(dst Destination, src []byte, opts *DecodeOptions) error {
m := Metadata{
ViewBox: DefaultViewBox,
Palette: DefaultPalette,
}
if opts != nil && opts.Palette != nil {
m.Palette = *opts.Palette
}
return decode(dst, nil, &m, false, src, opts)
}
func decode(dst Destination, p printer, m *Metadata, metadataOnly bool, src buffer, opts *DecodeOptions) (err error) {
if !bytes.HasPrefix(src, magicBytes) {
// TODO: detect FFV 1 (File Format Version 1), as opposed to the FFV 0
// that this package implements, and delegate to a FFV 1 decoder.
return errInvalidMagicIdentifier
}
if p != nil {
p(src[:len(magic)], "IconVG Magic identifier\n")
}
src = src[len(magic):]
nMetadataChunks, n := src.decodeNatural()
if n == 0 {
return errInvalidNumberOfMetadataChunks
}
if p != nil {
p(src[:n], "Number of metadata chunks: %d\n", nMetadataChunks)
}
src = src[n:]
for ; nMetadataChunks > 0; nMetadataChunks-- {
src, err = decodeMetadataChunk(p, m, src, opts)
if err != nil {
return err
}
}
if metadataOnly {
return nil
}
if dst != nil {
dst.Reset(*m)
}
mf := modeFunc(decodeStyling)
for len(src) > 0 {
mf, src, err = mf(dst, p, src)
if err != nil {
return err
}
}
return nil
}
func decodeMetadataChunk(p printer, m *Metadata, src buffer, opts *DecodeOptions) (src1 buffer, err error) {
length, n := src.decodeNatural()
if n == 0 {
return nil, errInvalidMetadataChunkLength
}
if p != nil {
p(src[:n], "Metadata chunk length: %d\n", length)
}
src = src[n:]
lenSrcWant := int64(len(src)) - int64(length)
mid, n := src.decodeNatural()
if n == 0 {
return nil, errInvalidMetadataIdentifier
}
if mid >= uint32(len(midDescriptions)) {
return nil, errUnsupportedMetadataIdentifier
}
if p != nil {
p(src[:n], "Metadata Identifier: %d (%s)\n", mid, midDescriptions[mid])
}
src = src[n:]
switch mid {
case midViewBox:
if m.ViewBox.Min[0], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
return nil, errInvalidViewBox
}
if m.ViewBox.Min[1], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
return nil, errInvalidViewBox
}
if m.ViewBox.Max[0], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
return nil, errInvalidViewBox
}
if m.ViewBox.Max[1], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
return nil, errInvalidViewBox
}
if m.ViewBox.Min[0] > m.ViewBox.Max[0] || m.ViewBox.Min[1] > m.ViewBox.Max[1] ||
isNaNOrInfinity(m.ViewBox.Min[0]) || isNaNOrInfinity(m.ViewBox.Min[1]) ||
isNaNOrInfinity(m.ViewBox.Max[0]) || isNaNOrInfinity(m.ViewBox.Max[1]) {
return nil, errInvalidViewBox
}
case midSuggestedPalette:
if len(src) == 0 {
return nil, errInvalidSuggestedPalette
}
length, format := 1+int(src[0]&0x3f), src[0]>>6
decode := buffer.decodeColor4
switch format {
case 0:
decode = buffer.decodeColor1
case 1:
decode = buffer.decodeColor2
case 2:
decode = buffer.decodeColor3Direct
}
if p != nil {
p(src[:1], " %d palette colors, %d bytes per color\n", length, 1+format)
}
src = src[1:]
for i := 0; i < length; i++ {
c, n := decode(src)
if n == 0 {
return nil, errInvalidSuggestedPalette
}
rgba := c.rgba()
if c.typ != ColorTypeRGBA || !validAlphaPremulColor(rgba) {
rgba = color.RGBA{0x00, 0x00, 0x00, 0xff}
}
if p != nil {
p(src[:n], " RGBA %02x%02x%02x%02x\n", rgba.R, rgba.G, rgba.B, rgba.A)
}
src = src[n:]
if opts == nil || opts.Palette == nil {
m.Palette[i] = rgba
}
}
default:
return nil, errUnsupportedMetadataIdentifier
}
if int64(len(src)) != lenSrcWant {
return nil, errInconsistentMetadataChunkLength
}
return src, nil
}
// modeFunc is the decoding mode: whether we are decoding styling or drawing
// opcodes.
//
// It is a function type. The decoding loop calls this function to decode and
// execute the next opcode from the src buffer, returning the subsequent mode
// and the remaining source bytes.
type modeFunc func(dst Destination, p printer, src buffer) (modeFunc, buffer, error)
func decodeStyling(dst Destination, p printer, src buffer) (modeFunc, buffer, error) {
switch opcode := src[0]; {
case opcode < 0x80:
if opcode < 0x40 {
opcode &= 0x3f
if p != nil {
p(src[:1], "Set CSEL = %d\n", opcode)
}
src = src[1:]
if dst != nil {
dst.SetCSel(opcode)
}
} else {
opcode &= 0x3f
if p != nil {
p(src[:1], "Set NSEL = %d\n", opcode)
}
src = src[1:]
if dst != nil {
dst.SetNSel(opcode)
}
}
return decodeStyling, src, nil
case opcode < 0xa8:
return decodeSetCReg(dst, p, src, opcode)
case opcode < 0xc0:
return decodeSetNReg(dst, p, src, opcode)
case opcode < 0xc7:
return decodeStartPath(dst, p, src, opcode)
case opcode == 0xc7:
return decodeSetLOD(dst, p, src)
}
return nil, nil, errUnsupportedStylingOpcode
}
func decodeSetCReg(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
nBytes, directness, adj := 0, "", opcode&0x07
var decode func(buffer) (Color, int)
incr := adj == 7
if incr {
adj = 0
}
switch (opcode - 0x80) >> 3 {
case 0:
nBytes, directness, decode = 1, "", buffer.decodeColor1
case 1:
nBytes, directness, decode = 2, "", buffer.decodeColor2
case 2:
nBytes, directness, decode = 3, " (direct)", buffer.decodeColor3Direct
case 3:
nBytes, directness, decode = 4, "", buffer.decodeColor4
case 4:
nBytes, directness, decode = 3, " (indirect)", buffer.decodeColor3Indirect
}
if p != nil {
if incr {
p(src[:1], "Set CREG[CSEL-0] to a %d byte%s color; CSEL++\n", nBytes, directness)
} else {
p(src[:1], "Set CREG[CSEL-%d] to a %d byte%s color\n", adj, nBytes, directness)
}
}
src = src[1:]
c, n := decode(src)
if n == 0 {
return nil, nil, errInvalidColor
}
if p != nil {
printColor(src[:n], p, c, "")
}
src = src[n:]
if dst != nil {
dst.SetCReg(adj, incr, c)
}
return decodeStyling, src, nil
}
func printColor(src []byte, p printer, c Color, prefix string) {
switch c.typ {
case ColorTypeRGBA:
if rgba := c.rgba(); validAlphaPremulColor(rgba) {
p(src, " %sRGBA %02x%02x%02x%02x\n", prefix, rgba.R, rgba.G, rgba.B, rgba.A)
} else if rgba.A == 0 && rgba.B&0x80 != 0 {
p(src, " %sgradient (NSTOPS=%d, CBASE=%d, NBASE=%d, %s, %s)\n",
prefix,
rgba.R&0x3f,
rgba.G&0x3f,
rgba.B&0x3f,
gradientShapeNames[(rgba.B>>6)&0x01],
gradientSpreadNames[rgba.G>>6],
)
} else {
p(src, " %snonsensical color\n", prefix)
}
case ColorTypePaletteIndex:
p(src, " %scustomPalette[%d]\n", prefix, c.paletteIndex())
case ColorTypeCReg:
p(src, " %sCREG[%d]\n", prefix, c.cReg())
case ColorTypeBlend:
t, c0, c1 := c.blend()
p(src[:1], " blend %d:%d c0:c1\n", 0xff-t, t)
printColor(src[1:2], p, decodeColor1(c0), " c0: ")
printColor(src[2:3], p, decodeColor1(c1), " c1: ")
}
}
func decodeSetNReg(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
decode, typ, adj := buffer.decodeZeroToOne, "zero-to-one", opcode&0x07
incr := adj == 7
if incr {
adj = 0
}
switch (opcode - 0xa8) >> 3 {
case 0:
decode, typ = buffer.decodeReal, "real"
case 1:
decode, typ = buffer.decodeCoordinate, "coordinate"
}
if p != nil {
if incr {
p(src[:1], "Set NREG[NSEL-0] to a %s number; NSEL++\n", typ)
} else {
p(src[:1], "Set NREG[NSEL-%d] to a %s number\n", adj, typ)
}
}
src = src[1:]
f, n := decode(src)
if n == 0 {
return nil, nil, errInvalidNumber
}
if p != nil {
p(src[:n], " %g\n", f)
}
src = src[n:]
if dst != nil {
dst.SetNReg(adj, incr, f)
}
return decodeStyling, src, nil
}
func decodeStartPath(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
adj := opcode & 0x07
if p != nil {
p(src[:1], "Start path, filled with CREG[CSEL-%d]; M (absolute moveTo)\n", adj)
}
src = src[1:]
x, src, err := decodeNumber(p, src, buffer.decodeCoordinate)
if err != nil {
return nil, nil, err
}
y, src, err := decodeNumber(p, src, buffer.decodeCoordinate)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.StartPath(adj, x, y)
}
return decodeDrawing, src, nil
}
func decodeSetLOD(dst Destination, p printer, src buffer) (modeFunc, buffer, error) {
if p != nil {
p(src[:1], "Set LOD\n")
}
src = src[1:]
lod0, src, err := decodeNumber(p, src, buffer.decodeReal)
if err != nil {
return nil, nil, err
}
lod1, src, err := decodeNumber(p, src, buffer.decodeReal)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.SetLOD(lod0, lod1)
}
return decodeStyling, src, nil
}
func decodeDrawing(dst Destination, p printer, src buffer) (mf modeFunc, src1 buffer, err error) {
var coords [6]float32
switch opcode := src[0]; {
case opcode < 0xe0:
op, nCoords, nReps := "", 0, 1+int(opcode&0x0f)
switch opcode >> 4 {
case 0x00, 0x01:
op = "L (absolute lineTo)"
nCoords = 2
nReps = 1 + int(opcode&0x1f)
case 0x02, 0x03:
op = "l (relative lineTo)"
nCoords = 2
nReps = 1 + int(opcode&0x1f)
case 0x04:
op = "T (absolute smooth quadTo)"
nCoords = 2
case 0x05:
op = "t (relative smooth quadTo)"
nCoords = 2
case 0x06:
op = "Q (absolute quadTo)"
nCoords = 4
case 0x07:
op = "q (relative quadTo)"
nCoords = 4
case 0x08:
op = "S (absolute smooth cubeTo)"
nCoords = 4
case 0x09:
op = "s (relative smooth cubeTo)"
nCoords = 4
case 0x0a:
op = "C (absolute cubeTo)"
nCoords = 6
case 0x0b:
op = "c (relative cubeTo)"
nCoords = 6
case 0x0c:
op = "A (absolute arcTo)"
nCoords = 0
case 0x0d:
op = "a (relative arcTo)"
nCoords = 0
}
if p != nil {
p(src[:1], "%s, %d reps\n", op, nReps)
}
src = src[1:]
for i := 0; i < nReps; i++ {
if p != nil && i != 0 {
p(nil, "%s, implicit\n", op)
}
var largeArc, sweep bool
if op[0] != 'A' && op[0] != 'a' {
src, err = decodeCoordinates(coords[:nCoords], p, src)
if err != nil {
return nil, nil, err
}
} else {
// We have an absolute or relative arcTo.
src, err = decodeCoordinates(coords[:2], p, src)
if err != nil {
return nil, nil, err
}
coords[2], src, err = decodeAngle(p, src)
if err != nil {
return nil, nil, err
}
largeArc, sweep, src, err = decodeArcToFlags(p, src)
if err != nil {
return nil, nil, err
}
src, err = decodeCoordinates(coords[4:6], p, src)
if err != nil {
return nil, nil, err
}
}
if dst == nil {
continue
}
switch op[0] {
case 'L':
dst.AbsLineTo(coords[0], coords[1])
case 'l':
dst.RelLineTo(coords[0], coords[1])
case 'T':
dst.AbsSmoothQuadTo(coords[0], coords[1])
case 't':
dst.RelSmoothQuadTo(coords[0], coords[1])
case 'Q':
dst.AbsQuadTo(coords[0], coords[1], coords[2], coords[3])
case 'q':
dst.RelQuadTo(coords[0], coords[1], coords[2], coords[3])
case 'S':
dst.AbsSmoothCubeTo(coords[0], coords[1], coords[2], coords[3])
case 's':
dst.RelSmoothCubeTo(coords[0], coords[1], coords[2], coords[3])
case 'C':
dst.AbsCubeTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])
case 'c':
dst.RelCubeTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])
case 'A':
dst.AbsArcTo(coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
case 'a':
dst.RelArcTo(coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
}
}
case opcode == 0xe1:
if p != nil {
p(src[:1], "z (closePath); end path\n")
}
src = src[1:]
if dst != nil {
dst.ClosePathEndPath()
}
return decodeStyling, src, nil
case opcode == 0xe2:
if p != nil {
p(src[:1], "z (closePath); M (absolute moveTo)\n")
}
src = src[1:]
src, err = decodeCoordinates(coords[:2], p, src)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.ClosePathAbsMoveTo(coords[0], coords[1])
}
case opcode == 0xe3:
if p != nil {
p(src[:1], "z (closePath); m (relative moveTo)\n")
}
src = src[1:]
src, err = decodeCoordinates(coords[:2], p, src)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.ClosePathRelMoveTo(coords[0], coords[1])
}
case opcode == 0xe6:
if p != nil {
p(src[:1], "H (absolute horizontal lineTo)\n")
}
src = src[1:]
src, err = decodeCoordinates(coords[:1], p, src)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.AbsHLineTo(coords[0])
}
case opcode == 0xe7:
if p != nil {
p(src[:1], "h (relative horizontal lineTo)\n")
}
src = src[1:]
src, err = decodeCoordinates(coords[:1], p, src)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.RelHLineTo(coords[0])
}
case opcode == 0xe8:
if p != nil {
p(src[:1], "V (absolute vertical lineTo)\n")
}
src = src[1:]
src, err = decodeCoordinates(coords[:1], p, src)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.AbsVLineTo(coords[0])
}
case opcode == 0xe9:
if p != nil {
p(src[:1], "v (relative vertical lineTo)\n")
}
src = src[1:]
src, err = decodeCoordinates(coords[:1], p, src)
if err != nil {
return nil, nil, err
}
if dst != nil {
dst.RelVLineTo(coords[0])
}
default:
return nil, nil, errUnsupportedDrawingOpcode
}
return decodeDrawing, src, nil
}
type decodeNumberFunc func(buffer) (float32, int)
func decodeNumber(p printer, src buffer, dnf decodeNumberFunc) (float32, buffer, error) {
x, n := dnf(src)
if n == 0 {
return 0, nil, errInvalidNumber
}
if p != nil {
p(src[:n], " %+g\n", x)
}
return x, src[n:], nil
}
func decodeCoordinates(coords []float32, p printer, src buffer) (src1 buffer, err error) {
for i := range coords {
coords[i], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
if err != nil {
return nil, err
}
}
return src, nil
}
func decodeCoordinatePairs(coords [][2]float32, p printer, src buffer) (src1 buffer, err error) {
for i := range coords {
coords[i][0], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
if err != nil {
return nil, err
}
coords[i][1], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
if err != nil {
return nil, err
}
}
return src, nil
}
func decodeAngle(p printer, src buffer) (float32, buffer, error) {
x, n := src.decodeZeroToOne()
if n == 0 {
return 0, nil, errInvalidNumber
}
if p != nil {
p(src[:n], " %v × 360 degrees (%v degrees)\n", x, x*360)
}
return x, src[n:], nil
}
func decodeArcToFlags(p printer, src buffer) (bool, bool, buffer, error) {
x, n := src.decodeNatural()
if n == 0 {
return false, false, nil, errInvalidNumber
}
if p != nil {
p(src[:n], " %#x (largeArc=%d, sweep=%d)\n", x, (x>>0)&0x01, (x>>1)&0x01)
}
return (x>>0)&0x01 != 0, (x>>1)&0x01 != 0, src[n:], nil
}
+31
View File
@@ -0,0 +1,31 @@
// 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.
A longer overview is at
https://github.com/google/iconvg
The file format is specified at
https://github.com/google/iconvg/blob/main/spec/iconvg-spec.md
This package's encoder emits byte-identical output for the same input,
independent of the platform (and specifically its floating-point hardware).
*/
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?
+605
View File
@@ -0,0 +1,605 @@
// 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
import (
"errors"
"image/color"
"math"
"golang.org/x/image/math/f32"
)
var (
errCSELUsedAsBothGradientAndStop = errors.New("iconvg: CSEL used as both gradient and stop")
errDrawingOpsUsedInStylingMode = errors.New("iconvg: drawing ops used in styling mode")
errInvalidSelectorAdjustment = errors.New("iconvg: invalid selector adjustment")
errInvalidIncrementingAdjustment = errors.New("iconvg: invalid incrementing adjustment")
errStylingOpsUsedInDrawingMode = errors.New("iconvg: styling ops used in drawing mode")
errTooManyGradientStops = errors.New("iconvg: too many gradient stops")
)
type mode uint8
const (
modeInitial mode = iota
modeStyling
modeDrawing
)
// Encoder is an IconVG encoder.
//
// The zero value is usable. Calling Reset, which is optional, sets the
// Metadata for the subsequent encoded form. If Reset is not called before
// other Encoder methods, the default metadata is implied.
//
// It aims to emit byte-identical Bytes output for the same input, independent
// of the platform (and specifically its floating-point hardware).
type Encoder struct {
// HighResolutionCoordinates is whether the encoder should encode
// coordinate numbers for subsequent paths at the best possible resolution
// afforded by the underlying graphic format.
//
// By default (false), the encoder quantizes coordinates to 1/64th of a
// unit if possible (the default graphic size is 64 by 64 units, so
// 1/4096th of the default width or height). Each such coordinate can
// therefore be encoded in either 1 or 2 bytes. If true, some coordinates
// will be encoded in 4 bytes, giving greater accuracy but larger file
// sizes. On the Material Design icon set, the 950 or so icons take up
// around 40% more bytes (172K vs 123K) at high resolution.
//
// See the package documentation for more details on the coordinate number
// encoding format.
HighResolutionCoordinates bool
// highResolutionCoordinates is a local copy, copied during StartPath, to
// avoid having to specify the semantics of modifying the exported field
// while drawing.
highResolutionCoordinates bool
buf buffer
altBuf buffer
metadata Metadata
err error
lod0 float32
lod1 float32
cSel uint8
nSel uint8
mode mode
drawOp byte
drawArgs []float32
scratch [12]byte
}
// Bytes returns the encoded form.
func (e *Encoder) Bytes() ([]byte, error) {
if e.err != nil {
return nil, e.err
}
if e.mode == modeInitial {
e.appendDefaultMetadata()
}
return []byte(e.buf), nil
}
// Reset resets the Encoder for the given Metadata.
//
// This includes setting e.HighResolutionCoordinates to false.
func (e *Encoder) Reset(m Metadata) {
*e = Encoder{
buf: append(e.buf[:0], magic...),
metadata: m,
mode: modeStyling,
lod1: positiveInfinity,
}
nMetadataChunks := 0
mcViewBox := m.ViewBox != DefaultViewBox
if mcViewBox {
nMetadataChunks++
}
mcSuggestedPalette := m.Palette != DefaultPalette
if mcSuggestedPalette {
nMetadataChunks++
}
e.buf.encodeNatural(uint32(nMetadataChunks))
if mcViewBox {
e.altBuf = e.altBuf[:0]
e.altBuf.encodeNatural(midViewBox)
e.altBuf.encodeCoordinate(m.ViewBox.Min[0])
e.altBuf.encodeCoordinate(m.ViewBox.Min[1])
e.altBuf.encodeCoordinate(m.ViewBox.Max[0])
e.altBuf.encodeCoordinate(m.ViewBox.Max[1])
e.buf.encodeNatural(uint32(len(e.altBuf)))
e.buf = append(e.buf, e.altBuf...)
}
if mcSuggestedPalette {
n := 63
for ; n >= 0 && m.Palette[n] == (color.RGBA{0x00, 0x00, 0x00, 0xff}); n-- {
}
// Find the shortest encoding that can represent all of m.Palette's n+1
// explicit colors.
enc1, enc2, enc3 := true, true, true
for _, c := range m.Palette[:n+1] {
if enc1 && (!is1(c.R) || !is1(c.G) || !is1(c.B) || !is1(c.A)) {
enc1 = false
}
if enc2 && (!is2(c.R) || !is2(c.G) || !is2(c.B) || !is2(c.A)) {
enc2 = false
}
if enc3 && (c.A != 0xff) {
enc3 = false
}
}
e.altBuf = e.altBuf[:0]
e.altBuf.encodeNatural(midSuggestedPalette)
if enc1 {
e.altBuf = append(e.altBuf, byte(n)|0x00)
for _, c := range m.Palette[:n+1] {
x, _ := encodeColor1(RGBAColor(c))
e.altBuf = append(e.altBuf, x)
}
} else if enc2 {
e.altBuf = append(e.altBuf, byte(n)|0x40)
for _, c := range m.Palette[:n+1] {
x, _ := encodeColor2(RGBAColor(c))
e.altBuf = append(e.altBuf, x[0], x[1])
}
} else if enc3 {
e.altBuf = append(e.altBuf, byte(n)|0x80)
for _, c := range m.Palette[:n+1] {
e.altBuf = append(e.altBuf, c.R, c.G, c.B)
}
} else {
e.altBuf = append(e.altBuf, byte(n)|0xc0)
for _, c := range m.Palette[:n+1] {
e.altBuf = append(e.altBuf, c.R, c.G, c.B, c.A)
}
}
e.buf.encodeNatural(uint32(len(e.altBuf)))
e.buf = append(e.buf, e.altBuf...)
}
}
func (e *Encoder) appendDefaultMetadata() {
e.buf = append(e.buf[:0], magic...)
e.buf = append(e.buf, 0x00) // There are zero metadata chunks.
e.mode = modeStyling
}
func (e *Encoder) CSel() uint8 {
if e.mode == modeInitial {
e.appendDefaultMetadata()
}
return e.cSel
}
func (e *Encoder) NSel() uint8 {
if e.mode == modeInitial {
e.appendDefaultMetadata()
}
return e.nSel
}
func (e *Encoder) LOD() (lod0, lod1 float32) {
if e.mode == modeInitial {
e.appendDefaultMetadata()
}
return e.lod0, e.lod1
}
func (e *Encoder) checkModeStyling() {
if e.mode == modeStyling {
return
}
if e.mode == modeInitial {
e.appendDefaultMetadata()
return
}
e.err = errStylingOpsUsedInDrawingMode
}
func (e *Encoder) SetCSel(cSel uint8) {
e.checkModeStyling()
if e.err != nil {
return
}
e.cSel = cSel & 0x3f
e.buf = append(e.buf, e.cSel)
}
func (e *Encoder) SetNSel(nSel uint8) {
e.checkModeStyling()
if e.err != nil {
return
}
e.nSel = nSel & 0x3f
e.buf = append(e.buf, e.nSel|0x40)
}
func (e *Encoder) SetCReg(adj uint8, incr bool, c Color) {
e.checkModeStyling()
if e.err != nil {
return
}
if adj > 6 {
e.err = errInvalidSelectorAdjustment
return
}
if incr {
if adj != 0 {
e.err = errInvalidIncrementingAdjustment
}
adj = 7
}
if x, ok := encodeColor1(c); ok {
e.buf = append(e.buf, adj|0x80, x)
return
}
if x, ok := encodeColor2(c); ok {
e.buf = append(e.buf, adj|0x88, x[0], x[1])
return
}
if x, ok := encodeColor3Direct(c); ok {
e.buf = append(e.buf, adj|0x90, x[0], x[1], x[2])
return
}
if x, ok := encodeColor4(c); ok {
e.buf = append(e.buf, adj|0x98, x[0], x[1], x[2], x[3])
return
}
if x, ok := encodeColor3Indirect(c); ok {
e.buf = append(e.buf, adj|0xa0, x[0], x[1], x[2])
return
}
panic("unreachable")
}
func (e *Encoder) SetNReg(adj uint8, incr bool, f float32) {
e.checkModeStyling()
if e.err != nil {
return
}
if adj > 6 {
e.err = errInvalidSelectorAdjustment
return
}
if incr {
if adj != 0 {
e.err = errInvalidIncrementingAdjustment
}
adj = 7
}
// Try three different encodings and pick the shortest.
b := buffer(e.scratch[0:0])
opcode, iBest, nBest := uint8(0xa8), 0, b.encodeReal(f)
b = buffer(e.scratch[4:4])
if n := b.encodeCoordinate(f); n < nBest {
opcode, iBest, nBest = 0xb0, 4, n
}
b = buffer(e.scratch[8:8])
if n := b.encodeZeroToOne(f); n < nBest {
opcode, iBest, nBest = 0xb8, 8, n
}
e.buf = append(e.buf, adj|opcode)
e.buf = append(e.buf, e.scratch[iBest:iBest+nBest]...)
}
func (e *Encoder) SetLOD(lod0, lod1 float32) {
e.checkModeStyling()
if e.err != nil {
return
}
e.lod0 = lod0
e.lod1 = lod1
e.buf = append(e.buf, 0xc7)
e.buf.encodeReal(lod0)
e.buf.encodeReal(lod1)
}
// SetGradient sets CREG[CSEL] to encode the gradient whose colors defined by
// spread and stops. Its geometry is either linear or radial, depending on the
// radial argument, and the given affine transformation matrix maps from
// graphic coordinate space defined by the metadata's viewBox (e.g. from (-32,
// -32) to (+32, +32)) 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 colors of the n stops are encoded at CREG[cBase+0], CREG[cBase+1], ...,
// CREG[cBase+n-1]. Similarly, the offsets of the n stops are encoded at
// NREG[nBase+0], NREG[nBase+1], ..., NREG[nBase+n-1]. Additional parameters
// are stored at NREG[nBase-4], NREG[nBase-3], NREG[nBase-2] and NREG[nBase-1].
//
// The CSEL and NSEL selector registers maintain the same values after the
// method returns as they had when the method was called.
//
// See the package documentation for more details on the gradient encoding
// format and the derivation of common transformation matrices.
func (e *Encoder) SetGradient(cBase, nBase uint8, radial bool, transform f32.Aff3, spread GradientSpread, stops []GradientStop) {
e.checkModeStyling()
if e.err != nil {
return
}
if len(stops) > 64-len(transform) {
e.err = errTooManyGradientStops
return
}
if x, y := e.cSel, e.cSel+64; (cBase <= x && x < cBase+uint8(len(stops))) ||
(cBase <= y && y < cBase+uint8(len(stops))) {
e.err = errCSELUsedAsBothGradientAndStop
return
}
oldCSel := e.cSel
oldNSel := e.nSel
cBase &= 0x3f
nBase &= 0x3f
bFlags := uint8(0x80)
if radial {
bFlags = 0xc0
}
e.SetCReg(0, false, RGBAColor(color.RGBA{
R: uint8(len(stops)),
G: cBase | uint8(spread<<6),
B: nBase | bFlags,
A: 0x00,
}))
e.SetCSel(cBase)
e.SetNSel(nBase)
for i, v := range transform {
e.SetNReg(uint8(len(transform)-i), false, v)
}
for _, s := range stops {
r, g, b, a := s.Color.RGBA()
e.SetCReg(0, true, RGBAColor(color.RGBA{
R: uint8(r >> 8),
G: uint8(g >> 8),
B: uint8(b >> 8),
A: uint8(a >> 8),
}))
e.SetNReg(0, true, s.Offset)
}
e.SetCSel(oldCSel)
e.SetNSel(oldNSel)
}
// SetLinearGradient is like SetGradient with radial=false except that the
// transformation matrix is implicitly defined by two boundary points (x1, y1)
// and (x2, y2).
func (e *Encoder) SetLinearGradient(cBase, nBase uint8, x1, y1, x2, y2 float32, spread GradientSpread, stops []GradientStop) {
// See the package documentation's appendix for a derivation of the
// transformation matrix.
dx, dy := x2-x1, y2-y1
d := dx*dx + dy*dy
ma := dx / d
mb := dy / d
e.SetGradient(cBase, nBase, false, f32.Aff3{
ma, mb, -ma*x1 - mb*y1,
0, 0, 0,
}, spread, stops)
}
// SetCircularGradient is like SetGradient with radial=true except that the
// transformation matrix is implicitly defined by a center (cx, cy) and a
// radius vector (rx, ry) such that (cx+rx, cy+ry) is on the circle.
func (e *Encoder) SetCircularGradient(cBase, nBase uint8, cx, cy, rx, ry float32, spread GradientSpread, stops []GradientStop) {
// See the package documentation's appendix for a derivation of the
// transformation matrix.
invR := float32(1 / math.Sqrt(float64(rx*rx+ry*ry)))
e.SetGradient(cBase, nBase, true, f32.Aff3{
invR, 0, -cx * invR,
0, invR, -cy * invR,
}, spread, stops)
}
// SetEllipticalGradient is like SetGradient with radial=true except that the
// transformation matrix is implicitly defined by a center (cx, cy) and two
// axis vectors (rx, ry) and (sx, sy) such that (cx+rx, cy+ry) and (cx+sx,
// cy+sy) are on the ellipse.
func (e *Encoder) SetEllipticalGradient(cBase, nBase uint8, cx, cy, rx, ry, sx, sy float32, spread GradientSpread, stops []GradientStop) {
// Explicitly disable FMA in the floating-point calculations below
// to get consistent results on all platforms, and in turn produce
// a byte-identical encoding.
// See https://golang.org/ref/spec#Floating_point_operators and issue 43219.
// See the package documentation's appendix for a derivation of the
// transformation matrix.
invRSSR := 1 / (float32(rx*sy) - float32(sx*ry))
ma := +sy * invRSSR
mb := -sx * invRSSR
mc := -float32(ma*cx) - float32(mb*cy)
md := -ry * invRSSR
me := +rx * invRSSR
mf := -float32(md*cx) - float32(me*cy)
e.SetGradient(cBase, nBase, true, f32.Aff3{
ma, mb, mc,
md, me, mf,
}, spread, stops)
}
func (e *Encoder) StartPath(adj uint8, x, y float32) {
e.checkModeStyling()
if e.err != nil {
return
}
if adj > 6 {
e.err = errInvalidSelectorAdjustment
return
}
e.highResolutionCoordinates = e.HighResolutionCoordinates
e.buf = append(e.buf, uint8(0xc0+adj))
e.buf.encodeCoordinate(quantize(x, e.highResolutionCoordinates))
e.buf.encodeCoordinate(quantize(y, e.highResolutionCoordinates))
e.mode = modeDrawing
}
func (e *Encoder) AbsHLineTo(x float32) { e.draw('H', x, 0, 0, 0, 0, 0) }
func (e *Encoder) RelHLineTo(x float32) { e.draw('h', x, 0, 0, 0, 0, 0) }
func (e *Encoder) AbsVLineTo(y float32) { e.draw('V', y, 0, 0, 0, 0, 0) }
func (e *Encoder) RelVLineTo(y float32) { e.draw('v', y, 0, 0, 0, 0, 0) }
func (e *Encoder) AbsLineTo(x, y float32) { e.draw('L', x, y, 0, 0, 0, 0) }
func (e *Encoder) RelLineTo(x, y float32) { e.draw('l', x, y, 0, 0, 0, 0) }
func (e *Encoder) AbsSmoothQuadTo(x, y float32) { e.draw('T', x, y, 0, 0, 0, 0) }
func (e *Encoder) RelSmoothQuadTo(x, y float32) { e.draw('t', x, y, 0, 0, 0, 0) }
func (e *Encoder) AbsQuadTo(x1, y1, x, y float32) { e.draw('Q', x1, y1, x, y, 0, 0) }
func (e *Encoder) RelQuadTo(x1, y1, x, y float32) { e.draw('q', x1, y1, x, y, 0, 0) }
func (e *Encoder) AbsSmoothCubeTo(x2, y2, x, y float32) { e.draw('S', x2, y2, x, y, 0, 0) }
func (e *Encoder) RelSmoothCubeTo(x2, y2, x, y float32) { e.draw('s', x2, y2, x, y, 0, 0) }
func (e *Encoder) AbsCubeTo(x1, y1, x2, y2, x, y float32) { e.draw('C', x1, y1, x2, y2, x, y) }
func (e *Encoder) RelCubeTo(x1, y1, x2, y2, x, y float32) { e.draw('c', x1, y1, x2, y2, x, y) }
func (e *Encoder) ClosePathEndPath() { e.draw('Z', 0, 0, 0, 0, 0, 0) }
func (e *Encoder) ClosePathAbsMoveTo(x, y float32) { e.draw('Y', x, y, 0, 0, 0, 0) }
func (e *Encoder) ClosePathRelMoveTo(x, y float32) { e.draw('y', x, y, 0, 0, 0, 0) }
func (e *Encoder) AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
e.arcTo('A', rx, ry, xAxisRotation, largeArc, sweep, x, y)
}
func (e *Encoder) RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
e.arcTo('a', rx, ry, xAxisRotation, largeArc, sweep, x, y)
}
func (e *Encoder) arcTo(drawOp byte, rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
flags := uint32(0)
if largeArc {
flags |= 0x01
}
if sweep {
flags |= 0x02
}
e.draw(drawOp, rx, ry, xAxisRotation, float32(flags), x, y)
}
func (e *Encoder) draw(drawOp byte, arg0, arg1, arg2, arg3, arg4, arg5 float32) {
if e.err != nil {
return
}
if e.mode != modeDrawing {
e.err = errDrawingOpsUsedInStylingMode
return
}
if e.drawOp != drawOp {
e.flushDrawOps()
}
e.drawOp = drawOp
switch drawOps[drawOp].nArgs {
case 0:
// No-op.
case 1:
e.drawArgs = append(e.drawArgs, arg0)
case 2:
e.drawArgs = append(e.drawArgs, arg0, arg1)
case 4:
e.drawArgs = append(e.drawArgs, arg0, arg1, arg2, arg3)
case 6:
e.drawArgs = append(e.drawArgs, arg0, arg1, arg2, arg3, arg4, arg5)
default:
panic("unreachable")
}
switch drawOp {
case 'Z':
e.mode = modeStyling
fallthrough
case 'Y', 'y':
e.flushDrawOps()
}
}
func (e *Encoder) flushDrawOps() {
if e.drawOp == 0x00 {
return
}
if op := drawOps[e.drawOp]; op.nArgs == 0 {
e.buf = append(e.buf, op.opcodeBase)
} else {
n := len(e.drawArgs) / int(op.nArgs)
for i := 0; n > 0; {
m := n
if m > int(op.maxRepCount) {
m = int(op.maxRepCount)
}
e.buf = append(e.buf, op.opcodeBase+uint8(m)-1)
switch e.drawOp {
default:
for j := m * int(op.nArgs); j > 0; j-- {
e.buf.encodeCoordinate(quantize(e.drawArgs[i], e.highResolutionCoordinates))
i++
}
case 'A', 'a':
for j := m; j > 0; j-- {
e.buf.encodeCoordinate(quantize(e.drawArgs[i+0], e.highResolutionCoordinates))
e.buf.encodeCoordinate(quantize(e.drawArgs[i+1], e.highResolutionCoordinates))
e.buf.encodeAngle(e.drawArgs[i+2])
e.buf.encodeNatural(uint32(e.drawArgs[i+3]))
e.buf.encodeCoordinate(quantize(e.drawArgs[i+4], e.highResolutionCoordinates))
e.buf.encodeCoordinate(quantize(e.drawArgs[i+5], e.highResolutionCoordinates))
i += 6
}
}
n -= m
}
}
e.drawOp = 0x00
e.drawArgs = e.drawArgs[:0]
}
func quantize(coord float32, highResolutionCoordinates bool) float32 {
if !highResolutionCoordinates && (-128 <= coord && coord < 128) {
x := math.Floor(float64(coord*64 + 0.5))
return float32(x) / 64
}
return coord
}
var drawOps = [256]struct {
opcodeBase byte
maxRepCount uint8
nArgs uint8
}{
'L': {0x00, 32, 2},
'l': {0x20, 32, 2},
'T': {0x40, 16, 2},
't': {0x50, 16, 2},
'Q': {0x60, 16, 4},
'q': {0x70, 16, 4},
'S': {0x80, 16, 4},
's': {0x90, 16, 4},
'C': {0xa0, 16, 6},
'c': {0xb0, 16, 6},
'A': {0xc0, 16, 6},
'a': {0xd0, 16, 6},
// Z means close path and then end path.
'Z': {0xe1, 1, 0},
// Y/y means close path and then open a new path (with a MoveTo/moveTo).
'Y': {0xe2, 1, 2},
'y': {0xe3, 1, 2},
'H': {0xe6, 1, 1},
'h': {0xe7, 1, 1},
'V': {0xe8, 1, 1},
'v': {0xe9, 1, 1},
}
+163
View File
@@ -0,0 +1,163 @@
// 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
import (
"image/color"
"math"
"golang.org/x/image/math/f32"
)
const magic = "\x89IVG"
var magicBytes = []byte(magic)
var (
negativeInfinity = math.Float32frombits(0xff800000)
positiveInfinity = math.Float32frombits(0x7f800000)
)
func isNaNOrInfinity(f float32) bool {
return math.Float32bits(f)&0x7f800000 == 0x7f800000
}
const (
// File Format Version 0.
midViewBox = 0
midSuggestedPalette = 1
// File Format Version 1.
ffv1MIDViewBox = 8
ffv1MIDSuggestedPalette = 16
)
var gradientShapeNames = [2]string{
"linear",
"radial",
}
var gradientSpreadNames = [4]string{
"none",
"pad",
"reflect",
"repeat",
}
// GradientSpread is how to spread a gradient past its nominal bounds (from
// offset being 0.0 to offset being 1.0).
type GradientSpread uint8
const (
GradientSpreadNone GradientSpread = 0
GradientSpreadPad GradientSpread = 1
GradientSpreadReflect GradientSpread = 2
GradientSpreadRepeat GradientSpread = 3
)
// GradientStop is a color/offset gradient stop.
type GradientStop struct {
Offset float32
Color color.Color
}
// Rectangle is defined by its minimum and maximum coordinates.
type Rectangle struct {
Min, Max f32.Vec2
}
// AspectRatio returns the Rectangle's aspect ratio. An IconVG graphic is
// scalable; these dimensions do not necessarily map 1:1 to pixels.
func (r *Rectangle) AspectRatio() (dx, dy float32) {
return r.Max[0] - r.Min[0], r.Max[1] - r.Min[1]
}
// Palette is an IconVG palette.
type Palette [64]color.RGBA
// Metadata is an IconVG's metadata.
type Metadata struct {
ViewBox Rectangle
// Palette is a 64 color palette. When encoding, it is the suggested
// palette to place within the IconVG graphic. When decoding, it is either
// the optional palette passed to Decode, or if no optional palette was
// given, the suggested palette within the IconVG graphic.
Palette Palette
}
// DefaultViewBox is the default ViewBox. Its values should not be modified.
var DefaultViewBox = Rectangle{
Min: f32.Vec2{-32, -32},
Max: f32.Vec2{+32, +32},
}
// DefaultPalette is the default Palette. Its values should not be modified.
var DefaultPalette = Palette{
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0x00, 0xff},
}
+242
View File
@@ -0,0 +1,242 @@
// 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 gradient provides linear and radial gradient images.
package gradient
import (
"image"
"image/color"
"math"
"golang.org/x/image/math/f64"
)
// TODO: gamma correction / non-linear color interpolation?
// TODO: move this out of an internal directory, either under
// golang.org/x/image or under the standard library's image, so that
// golang.org/x/image/{draw,vector} and possibly image/draw can type switch on
// the gradient.Gradient type and provide fast path code.
//
// Doing so requires coming up with a stable API that we'd be happy to support
// in the long term. This would probably include an easier way to create
// linear, circular and elliptical gradients, without having to explicitly
// calculate the f64.Aff3 matrix.
// Shape is the gradient shape.
type Shape uint8
const (
ShapeLinear Shape = iota
ShapeRadial
)
// Spread is the gradient spread, or how to spread a gradient past its nominal
// bounds (from offset being 0.0 to offset being 1.0).
type Spread uint8
const (
// SpreadNone means that offsets outside of the [0, 1] range map to
// transparent black.
SpreadNone Spread = iota
// SpreadPad means that offsets below 0 and above 1 map to the colors that
// 0 and 1 would map to.
SpreadPad
// SpreadReflect means that the offset mapping is reflected start-to-end,
// end-to-start, start-to-end, etc.
SpreadReflect
// SpreadRepeat means that the offset mapping is repeated start-to-end,
// start-to-end, start-to-end, etc.
SpreadRepeat
)
// Clamp clamps x to the range [0, 1]. If x is outside that range, it is
// converted to a value in that range according to s's semantics. It returns -1
// if s is SpreadNone and x is outside the range [0, 1].
func (s Spread) Clamp(x float64) float64 {
if x >= 0 {
if x <= 1 {
return x
}
switch s {
case SpreadPad:
return 1
case SpreadReflect:
if int(x)&1 == 0 {
return x - math.Floor(x)
}
return math.Ceil(x) - x
case SpreadRepeat:
return x - math.Floor(x)
}
return -1
}
switch s {
case SpreadPad:
return 0
case SpreadReflect:
x = -x
if int(x)&1 == 0 {
return x - math.Floor(x)
}
return math.Ceil(x) - x
case SpreadRepeat:
return x - math.Floor(x)
}
return -1
}
// Stop is an offset and color.
type Stop struct {
Offset float64
RGBA64 color.RGBA64
}
// Range is the range between two stops.
type Range struct {
Offset0 float64
Offset1 float64
Width float64
R0 float64
R1 float64
G0 float64
G1 float64
B0 float64
B1 float64
A0 float64
A1 float64
}
// MakeRange returns the range between two stops.
func MakeRange(s0, s1 Stop) Range {
return Range{
Offset0: s0.Offset,
Offset1: s1.Offset,
Width: s1.Offset - s0.Offset,
R0: float64(s0.RGBA64.R),
R1: float64(s1.RGBA64.R),
G0: float64(s0.RGBA64.G),
G1: float64(s1.RGBA64.G),
B0: float64(s0.RGBA64.B),
B1: float64(s1.RGBA64.B),
A0: float64(s0.RGBA64.A),
A1: float64(s1.RGBA64.A),
}
}
// AppendRanges appends to a the ranges defined by a's implicit final stop (if
// any exist) and stops.
func AppendRanges(a []Range, stops []Stop) []Range {
if len(stops) == 0 {
return nil
}
if len(a) != 0 {
z := a[len(a)-1]
a = append(a, MakeRange(Stop{
Offset: z.Offset1,
RGBA64: color.RGBA64{
R: uint16(z.R1),
G: uint16(z.G1),
B: uint16(z.B1),
A: uint16(z.A1),
},
}, stops[0]))
}
for i := 0; i < len(stops)-1; i++ {
a = append(a, MakeRange(stops[i], stops[i+1]))
}
return a
}
// Gradient is a very large image.Image (the same size as an image.Uniform)
// whose colors form a gradient.
type Gradient struct {
Shape Shape
Spread Spread
// Pix2Grad transforms coordinates from pixel space (the arguments to the
// Image.At method) to gradient space. Gradient space is where a linear
// gradient ranges from x == 0 to x == 1, and a radial gradient has center
// (0, 0) and radius 1.
//
// This is an affine transform, so it can represent elliptical gradients in
// pixel space, including non-axis-aligned ellipses.
//
// For a linear gradient, the bottom row is ignored.
Pix2Grad f64.Aff3
Ranges []Range
// First and Last are the first and last stop's colors.
First, Last color.RGBA64
}
// Init initializes g to a gradient whose geometry is defined by shape and
// pix2Grad and whose colors are defined by spread and stops.
func (g *Gradient) Init(shape Shape, spread Spread, pix2Grad f64.Aff3, stops []Stop) {
g.Shape = shape
g.Spread = spread
g.Pix2Grad = pix2Grad
g.Ranges = AppendRanges(g.Ranges[:0], stops)
if len(stops) == 0 {
g.First = color.RGBA64{}
g.Last = color.RGBA64{}
} else {
g.First = stops[0].RGBA64
g.Last = stops[len(stops)-1].RGBA64
}
}
// ColorModel satisfies the image.Image interface.
func (g *Gradient) ColorModel() color.Model {
return color.RGBA64Model
}
// Bounds satisfies the image.Image interface.
func (g *Gradient) Bounds() image.Rectangle {
return image.Rectangle{
Min: image.Point{-1e9, -1e9},
Max: image.Point{+1e9, +1e9},
}
}
// At satisfies the image.Image interface.
func (g *Gradient) At(x, y int) color.Color {
if len(g.Ranges) == 0 {
return color.RGBA64{}
}
px := float64(x) + 0.5
py := float64(y) + 0.5
offset := 0.0
if g.Shape == ShapeLinear {
offset = g.Spread.Clamp(g.Pix2Grad[0]*px + g.Pix2Grad[1]*py + g.Pix2Grad[2])
} else {
gx := g.Pix2Grad[0]*px + g.Pix2Grad[1]*py + g.Pix2Grad[2]
gy := g.Pix2Grad[3]*px + g.Pix2Grad[4]*py + g.Pix2Grad[5]
offset = g.Spread.Clamp(math.Sqrt(gx*gx + gy*gy))
}
if !(offset >= 0) {
return color.RGBA64{}
}
if offset < g.Ranges[0].Offset0 {
return g.First
}
for _, r := range g.Ranges {
if r.Offset0 <= offset && offset <= r.Offset1 {
t := (offset - r.Offset0) / r.Width
s := 1 - t
return color.RGBA64{
uint16(s*r.R0 + t*r.R1),
uint16(s*r.G0 + t*r.G1),
uint16(s*r.B0 + t*r.B1),
uint16(s*r.A0 + t*r.A1),
}
}
}
return g.Last
}
+595
View File
@@ -0,0 +1,595 @@
// 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
import (
"image"
"image/color"
"image/draw"
"math"
"golang.org/x/exp/shiny/iconvg/internal/gradient"
"golang.org/x/image/math/f64"
"golang.org/x/image/vector"
)
const (
smoothTypeNone = iota
smoothTypeQuad
smoothTypeCube
)
// Rasterizer is a Destination that draws an IconVG graphic onto a raster
// image.
//
// The zero value is usable, in that it has no raster image to draw onto, so
// that calling Decode with this Destination is a no-op (other than checking
// the encoded form for errors in the byte code). Call SetDstImage to change
// the raster image, before calling Decode or between calls to Decode.
type Rasterizer struct {
z vector.Rasterizer
dst draw.Image
r image.Rectangle
drawOp draw.Op
// scale and bias transforms the metadata.ViewBox rectangle to the (0, 0) -
// (r.Dx(), r.Dy()) rectangle.
scaleX float32
biasX float32
scaleY float32
biasY float32
metadata Metadata
lod0 float32
lod1 float32
cSel uint8
nSel uint8
disabled bool
firstStartPath bool
prevSmoothType uint8
prevSmoothPointX float32
prevSmoothPointY float32
fill image.Image
flatColor color.RGBA
flatImage image.Uniform
gradient gradient.Gradient
cReg [64]color.RGBA
nReg [64]float32
stops [64]gradient.Stop
}
// SetDstImage sets the Rasterizer to draw onto a destination image, given by
// dst and r, with the given compositing operator.
//
// The IconVG graphic (which does not have a fixed size in pixels) will be
// scaled in the X and Y dimensions to fit the rectangle r. The scaling factors
// may differ in the two dimensions.
func (z *Rasterizer) SetDstImage(dst draw.Image, r image.Rectangle, drawOp draw.Op) {
z.dst = dst
if r.Empty() {
r = image.Rectangle{}
}
z.r = r
z.drawOp = drawOp
z.recalcTransform()
}
// Reset resets the Rasterizer for the given Metadata.
func (z *Rasterizer) Reset(m Metadata) {
z.metadata = m
z.lod0 = 0
z.lod1 = positiveInfinity
z.cSel = 0
z.nSel = 0
z.firstStartPath = true
z.prevSmoothType = smoothTypeNone
z.prevSmoothPointX = 0
z.prevSmoothPointY = 0
z.cReg = m.Palette
z.nReg = [64]float32{}
z.recalcTransform()
}
func (z *Rasterizer) recalcTransform() {
z.scaleX = float32(z.r.Dx()) / (z.metadata.ViewBox.Max[0] - z.metadata.ViewBox.Min[0])
z.biasX = -z.metadata.ViewBox.Min[0]
z.scaleY = float32(z.r.Dy()) / (z.metadata.ViewBox.Max[1] - z.metadata.ViewBox.Min[1])
z.biasY = -z.metadata.ViewBox.Min[1]
}
func (z *Rasterizer) SetCSel(cSel uint8) { z.cSel = cSel & 0x3f }
func (z *Rasterizer) SetNSel(nSel uint8) { z.nSel = nSel & 0x3f }
func (z *Rasterizer) SetCReg(adj uint8, incr bool, c Color) {
z.cReg[(z.cSel-adj)&0x3f] = c.Resolve(&z.metadata.Palette, &z.cReg)
if incr {
z.cSel++
}
}
func (z *Rasterizer) SetNReg(adj uint8, incr bool, f float32) {
z.nReg[(z.nSel-adj)&0x3f] = f
if incr {
z.nSel++
}
}
func (z *Rasterizer) SetLOD(lod0, lod1 float32) {
z.lod0, z.lod1 = lod0, lod1
}
func (z *Rasterizer) unabsX(x float32) float32 { return x/z.scaleX - z.biasX }
func (z *Rasterizer) unabsY(y float32) float32 { return y/z.scaleY - z.biasY }
func (z *Rasterizer) absX(x float32) float32 { return z.scaleX * (x + z.biasX) }
func (z *Rasterizer) absY(y float32) float32 { return z.scaleY * (y + z.biasY) }
func (z *Rasterizer) relX(x float32) float32 { return z.scaleX * x }
func (z *Rasterizer) relY(y float32) float32 { return z.scaleY * y }
func (z *Rasterizer) absVec2(x, y float32) (zx, zy float32) {
return z.absX(x), z.absY(y)
}
func (z *Rasterizer) relVec2(x, y float32) (zx, zy float32) {
px, py := z.z.Pen()
return px + z.relX(x), py + z.relY(y)
}
// implicitSmoothPoint returns the implicit control point for smooth-quadratic
// and smooth-cubic Bézier curves.
//
// https://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands says, "The first
// control point is assumed to be the reflection of the second control point on
// the previous command relative to the current point. (If there is no previous
// command or if the previous command was not [a quadratic or cubic command],
// assume the first control point is coincident with the current point.)"
func (z *Rasterizer) implicitSmoothPoint(thisSmoothType uint8) (zx, zy float32) {
px, py := z.z.Pen()
if z.prevSmoothType != thisSmoothType {
return px, py
}
return 2*px - z.prevSmoothPointX, 2*py - z.prevSmoothPointY
}
func (z *Rasterizer) initGradient(rgba color.RGBA) (ok bool) {
nStops := int(rgba.R & 0x3f)
cBase := int(rgba.G & 0x3f)
nBase := int(rgba.B & 0x3f)
prevN := negativeInfinity
for i := 0; i < nStops; i++ {
c := z.cReg[(cBase+i)&0x3f]
if !validAlphaPremulColor(c) {
return false
}
n := z.nReg[(nBase+i)&0x3f]
if !(0 <= n && n <= 1) || !(n > prevN) {
return false
}
prevN = n
z.stops[i] = gradient.Stop{
Offset: float64(n),
RGBA64: color.RGBA64{
R: uint16(c.R) * 0x101,
G: uint16(c.G) * 0x101,
B: uint16(c.B) * 0x101,
A: uint16(c.A) * 0x101,
},
}
}
// The affine transformation matrix in the IconVG graphic, stored in 6
// contiguous NREG registers, goes from graphic coordinate space (i.e. the
// metadata viewBox) to the gradient coordinate space. We need it to start
// in pixel space, not graphic coordinate space.
invZSX := 1 / float64(z.scaleX)
invZSY := 1 / float64(z.scaleY)
zBX := float64(z.biasX)
zBY := float64(z.biasY)
a := float64(z.nReg[(nBase-6)&0x3f])
b := float64(z.nReg[(nBase-5)&0x3f])
c := float64(z.nReg[(nBase-4)&0x3f])
d := float64(z.nReg[(nBase-3)&0x3f])
e := float64(z.nReg[(nBase-2)&0x3f])
f := float64(z.nReg[(nBase-1)&0x3f])
pix2Grad := f64.Aff3{
a * invZSX,
b * invZSY,
c - a*zBX - b*zBY,
d * invZSX,
e * invZSY,
f - d*zBX - e*zBY,
}
shape := gradient.ShapeLinear
if (rgba.B>>6)&0x01 != 0 {
shape = gradient.ShapeRadial
}
z.gradient.Init(
shape,
gradient.Spread(rgba.G>>6),
pix2Grad,
z.stops[:nStops],
)
return true
}
func (z *Rasterizer) StartPath(adj uint8, x, y float32) {
z.flatColor = z.cReg[(z.cSel-adj)&0x3f]
if validAlphaPremulColor(z.flatColor) {
z.flatImage.C = &z.flatColor
z.fill = &z.flatImage
z.disabled = z.flatColor.A == 0
} else if z.flatColor.A == 0x00 && z.flatColor.B&0x80 != 0 {
z.fill = &z.gradient
z.disabled = !z.initGradient(z.flatColor)
} else {
z.fill = nil
z.disabled = true
}
width, height := z.r.Dx(), z.r.Dy()
h := float32(height)
z.disabled = z.disabled || !(z.lod0 <= h && h < z.lod1)
if z.disabled {
return
}
z.z.Reset(width, height)
if z.firstStartPath {
z.firstStartPath = false
z.z.DrawOp = z.drawOp
}
z.prevSmoothType = smoothTypeNone
z.z.MoveTo(z.absVec2(x, y))
}
func (z *Rasterizer) ClosePathEndPath() {
if z.disabled {
return
}
z.z.ClosePath()
if z.dst == nil {
return
}
z.z.Draw(z.dst, z.r, z.fill, image.Point{})
}
func (z *Rasterizer) ClosePathAbsMoveTo(x, y float32) {
if z.disabled {
return
}
z.prevSmoothType = smoothTypeNone
z.z.ClosePath()
z.z.MoveTo(z.absVec2(x, y))
}
func (z *Rasterizer) ClosePathRelMoveTo(x, y float32) {
if z.disabled {
return
}
z.prevSmoothType = smoothTypeNone
z.z.ClosePath()
z.z.MoveTo(z.relVec2(x, y))
}
func (z *Rasterizer) AbsHLineTo(x float32) {
if z.disabled {
return
}
_, py := z.z.Pen()
z.prevSmoothType = smoothTypeNone
z.z.LineTo(z.absX(x), py)
}
func (z *Rasterizer) RelHLineTo(x float32) {
if z.disabled {
return
}
px, py := z.z.Pen()
z.prevSmoothType = smoothTypeNone
z.z.LineTo(px+z.relX(x), py)
}
func (z *Rasterizer) AbsVLineTo(y float32) {
if z.disabled {
return
}
px, _ := z.z.Pen()
z.prevSmoothType = smoothTypeNone
z.z.LineTo(px, z.absY(y))
}
func (z *Rasterizer) RelVLineTo(y float32) {
if z.disabled {
return
}
px, py := z.z.Pen()
z.prevSmoothType = smoothTypeNone
z.z.LineTo(px, py+z.relY(y))
}
func (z *Rasterizer) AbsLineTo(x, y float32) {
if z.disabled {
return
}
z.prevSmoothType = smoothTypeNone
z.z.LineTo(z.absVec2(x, y))
}
func (z *Rasterizer) RelLineTo(x, y float32) {
if z.disabled {
return
}
z.prevSmoothType = smoothTypeNone
z.z.LineTo(z.relVec2(x, y))
}
func (z *Rasterizer) AbsSmoothQuadTo(x, y float32) {
if z.disabled {
return
}
x1, y1 := z.implicitSmoothPoint(smoothTypeQuad)
x, y = z.absVec2(x, y)
z.prevSmoothType = smoothTypeQuad
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
z.z.QuadTo(x1, y1, x, y)
}
func (z *Rasterizer) RelSmoothQuadTo(x, y float32) {
if z.disabled {
return
}
x1, y1 := z.implicitSmoothPoint(smoothTypeQuad)
x, y = z.relVec2(x, y)
z.prevSmoothType = smoothTypeQuad
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
z.z.QuadTo(x1, y1, x, y)
}
func (z *Rasterizer) AbsQuadTo(x1, y1, x, y float32) {
if z.disabled {
return
}
x1, y1 = z.absVec2(x1, y1)
x, y = z.absVec2(x, y)
z.prevSmoothType = smoothTypeQuad
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
z.z.QuadTo(x1, y1, x, y)
}
func (z *Rasterizer) RelQuadTo(x1, y1, x, y float32) {
if z.disabled {
return
}
x1, y1 = z.relVec2(x1, y1)
x, y = z.relVec2(x, y)
z.prevSmoothType = smoothTypeQuad
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
z.z.QuadTo(x1, y1, x, y)
}
func (z *Rasterizer) AbsSmoothCubeTo(x2, y2, x, y float32) {
if z.disabled {
return
}
x1, y1 := z.implicitSmoothPoint(smoothTypeCube)
x2, y2 = z.absVec2(x2, y2)
x, y = z.absVec2(x, y)
z.prevSmoothType = smoothTypeCube
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
z.z.CubeTo(x1, y1, x2, y2, x, y)
}
func (z *Rasterizer) RelSmoothCubeTo(x2, y2, x, y float32) {
if z.disabled {
return
}
x1, y1 := z.implicitSmoothPoint(smoothTypeCube)
x2, y2 = z.relVec2(x2, y2)
x, y = z.relVec2(x, y)
z.prevSmoothType = smoothTypeCube
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
z.z.CubeTo(x1, y1, x2, y2, x, y)
}
func (z *Rasterizer) AbsCubeTo(x1, y1, x2, y2, x, y float32) {
if z.disabled {
return
}
x1, y1 = z.absVec2(x1, y1)
x2, y2 = z.absVec2(x2, y2)
x, y = z.absVec2(x, y)
z.prevSmoothType = smoothTypeCube
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
z.z.CubeTo(x1, y1, x2, y2, x, y)
}
func (z *Rasterizer) RelCubeTo(x1, y1, x2, y2, x, y float32) {
if z.disabled {
return
}
x1, y1 = z.relVec2(x1, y1)
x2, y2 = z.relVec2(x2, y2)
x, y = z.relVec2(x, y)
z.prevSmoothType = smoothTypeCube
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
z.z.CubeTo(x1, y1, x2, y2, x, y)
}
func (z *Rasterizer) AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
if z.disabled {
return
}
z.prevSmoothType = smoothTypeNone
// We follow the "Conversion from endpoint to center parameterization"
// algorithm as per
// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
// There seems to be a bug in the spec's "implementation notes".
//
// Actual implementations, such as
// - https://git.gnome.org/browse/librsvg/tree/rsvg-path.c
// - http://svn.apache.org/repos/asf/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/ext/awt/geom/ExtendedGeneralPath.java
// - https://java.net/projects/svgsalamander/sources/svn/content/trunk/svg-core/src/main/java/com/kitfox/svg/pathcmd/Arc.java
// - https://github.com/millermedeiros/SVGParser/blob/master/com/millermedeiros/geom/SVGArc.as
// do something slightly different (marked with a †).
// (†) The Abs isn't part of the spec. Neither is checking that Rx and Ry
// are non-zero (and non-NaN).
Rx := math.Abs(float64(rx))
Ry := math.Abs(float64(ry))
if !(Rx > 0 && Ry > 0) {
z.z.LineTo(x, y)
return
}
// We work in IconVG coordinates (e.g. from -32 to +32 by default), rather
// than destination image coordinates (e.g. the width of the dst image),
// since the rx and ry radii also need to be scaled, but their scaling
// factors can be different, and aren't trivial to calculate due to
// xAxisRotation.
//
// We convert back to destination image coordinates via absX and absY calls
// later, during arcSegmentTo.
penX, penY := z.z.Pen()
x1 := float64(z.unabsX(penX))
y1 := float64(z.unabsY(penY))
x2 := float64(x)
y2 := float64(y)
phi := 2 * math.Pi * float64(xAxisRotation)
// Step 1: Compute (x1′, y1′)
halfDx := (x1 - x2) / 2
halfDy := (y1 - y2) / 2
cosPhi := math.Cos(phi)
sinPhi := math.Sin(phi)
x1Prime := +cosPhi*halfDx + sinPhi*halfDy
y1Prime := -sinPhi*halfDx + cosPhi*halfDy
// Step 2: Compute (cx′, cy′)
rxSq := Rx * Rx
rySq := Ry * Ry
x1PrimeSq := x1Prime * x1Prime
y1PrimeSq := y1Prime * y1Prime
// (†) Check that the radii are large enough.
radiiCheck := x1PrimeSq/rxSq + y1PrimeSq/rySq
if radiiCheck > 1 {
c := math.Sqrt(radiiCheck)
Rx *= c
Ry *= c
rxSq = Rx * Rx
rySq = Ry * Ry
}
denom := rxSq*y1PrimeSq + rySq*x1PrimeSq
step2 := 0.0
if a := rxSq*rySq/denom - 1; a > 0 {
step2 = math.Sqrt(a)
}
if largeArc == sweep {
step2 = -step2
}
cxPrime := +step2 * Rx * y1Prime / Ry
cyPrime := -step2 * Ry * x1Prime / Rx
// Step 3: Compute (cx, cy) from (cx′, cy′)
cx := +cosPhi*cxPrime - sinPhi*cyPrime + (x1+x2)/2
cy := +sinPhi*cxPrime + cosPhi*cyPrime + (y1+y2)/2
// Step 4: Compute θ1 and Δθ
ax := (+x1Prime - cxPrime) / Rx
ay := (+y1Prime - cyPrime) / Ry
bx := (-x1Prime - cxPrime) / Rx
by := (-y1Prime - cyPrime) / Ry
theta1 := angle(1, 0, ax, ay)
deltaTheta := angle(ax, ay, bx, by)
if sweep {
if deltaTheta < 0 {
deltaTheta += 2 * math.Pi
}
} else {
if deltaTheta > 0 {
deltaTheta -= 2 * math.Pi
}
}
// This ends the
// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
// algorithm. What follows below is specific to this implementation.
// We approximate an arc by one or more cubic Bézier curves.
n := int(math.Ceil(math.Abs(deltaTheta) / (math.Pi/2 + 0.001)))
for i := 0; i < n; i++ {
z.arcSegmentTo(cx, cy,
theta1+deltaTheta*float64(i+0)/float64(n),
theta1+deltaTheta*float64(i+1)/float64(n),
Rx, Ry, cosPhi, sinPhi,
)
}
}
// arcSegmentTo approximates an arc by a cubic Bézier curve. The mathematical
// formulae for the control points are the same as that used by librsvg.
func (z *Rasterizer) arcSegmentTo(cx, cy, theta1, theta2, rx, ry, cosPhi, sinPhi float64) {
halfDeltaTheta := (theta2 - theta1) * 0.5
q := math.Sin(halfDeltaTheta * 0.5)
t := (8 * q * q) / (3 * math.Sin(halfDeltaTheta))
cos1 := math.Cos(theta1)
sin1 := math.Sin(theta1)
cos2 := math.Cos(theta2)
sin2 := math.Sin(theta2)
x1 := rx * (+cos1 - t*sin1)
y1 := ry * (+sin1 + t*cos1)
x2 := rx * (+cos2 + t*sin2)
y2 := ry * (+sin2 - t*cos2)
x3 := rx * (+cos2)
y3 := ry * (+sin2)
z.z.CubeTo(
z.absX(float32(cx+cosPhi*x1-sinPhi*y1)),
z.absY(float32(cy+sinPhi*x1+cosPhi*y1)),
z.absX(float32(cx+cosPhi*x2-sinPhi*y2)),
z.absY(float32(cy+sinPhi*x2+cosPhi*y2)),
z.absX(float32(cx+cosPhi*x3-sinPhi*y3)),
z.absY(float32(cy+sinPhi*x3+cosPhi*y3)),
)
}
func (z *Rasterizer) RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
ax, ay := z.relVec2(x, y)
z.AbsArcTo(rx, ry, xAxisRotation, largeArc, sweep, z.unabsX(ax), z.unabsY(ay))
}
// angle returns the angle between the u and v vectors.
func angle(ux, uy, vx, vy float64) float64 {
uNorm := math.Sqrt(ux*ux + uy*uy)
vNorm := math.Sqrt(vx*vx + vy*vy)
norm := uNorm * vNorm
cos := (ux*vx + uy*vy) / norm
ret := 0.0
if cos <= -1 {
ret = math.Pi
} else if cos >= +1 {
ret = 0
} else {
ret = math.Acos(cos)
}
if ux*vy < uy*vx {
return -ret
}
return +ret
}
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
// 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.
//go:generate go run gen.go -mdicons=/path/to/the/material-design-icons
// Package icons contains the Material Design icon set, in the IconVG vector
// graphic format.
//
// See https://design.google.com/icons/ and
// https://godoc.org/golang.org/x/exp/shiny/iconvg
package icons