From ece85d8884be111a014d1fa2ffeb9136f7774198 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 18 Nov 2021 04:21:37 +0100 Subject: [PATCH 01/13] add new dep --- go.mod | 3 + go.sum | 7 + .../codeberg.org/Codeberg/avatars/.gitignore | 2 + .../codeberg.org/Codeberg/avatars/README.md | 32 + .../codeberg.org/Codeberg/avatars/avatars.go | 536 +++++++ vendor/codeberg.org/Codeberg/avatars/go.mod | 3 + .../codeberg.org/Codeberg/avatars/mosaic.png | Bin 0 -> 309176 bytes vendor/github.com/srwiley/oksvg/.gitignore | 1 + vendor/github.com/srwiley/oksvg/LICENSE | 29 + vendor/github.com/srwiley/oksvg/README.md | 49 + vendor/github.com/srwiley/oksvg/svgd.go | 1299 +++++++++++++++++ vendor/github.com/srwiley/oksvg/svgp.go | 396 +++++ vendor/github.com/srwiley/rasterx/LICENSE | 29 + vendor/github.com/srwiley/rasterx/README.md | 84 ++ vendor/github.com/srwiley/rasterx/dash.go | 202 +++ vendor/github.com/srwiley/rasterx/fill.go | 241 +++ vendor/github.com/srwiley/rasterx/geomx.go | 301 ++++ vendor/github.com/srwiley/rasterx/gradient.go | 310 ++++ vendor/github.com/srwiley/rasterx/matrix.go | 191 +++ vendor/github.com/srwiley/rasterx/scan.go | 181 +++ vendor/github.com/srwiley/rasterx/shapes.go | 221 +++ vendor/github.com/srwiley/rasterx/stroke.go | 677 +++++++++ vendor/golang.org/x/image/AUTHORS | 3 + vendor/golang.org/x/image/CONTRIBUTORS | 3 + vendor/golang.org/x/image/LICENSE | 27 + vendor/golang.org/x/image/PATENTS | 22 + .../x/image/colornames/colornames.go | 10 + vendor/golang.org/x/image/colornames/table.go | 457 ++++++ vendor/golang.org/x/image/math/fixed/fixed.go | 410 ++++++ vendor/golang.org/x/image/vector/acc_amd64.go | 31 + vendor/golang.org/x/image/vector/acc_amd64.s | 1028 +++++++++++++ vendor/golang.org/x/image/vector/acc_other.go | 16 + .../x/image/vector/gen_acc_amd64.s.tmpl | 170 +++ .../golang.org/x/image/vector/raster_fixed.go | 327 +++++ .../x/image/vector/raster_floating.go | 220 +++ vendor/golang.org/x/image/vector/vector.go | 472 ++++++ vendor/modules.txt | 13 + 37 files changed, 8003 insertions(+) create mode 100644 vendor/codeberg.org/Codeberg/avatars/.gitignore create mode 100644 vendor/codeberg.org/Codeberg/avatars/README.md create mode 100644 vendor/codeberg.org/Codeberg/avatars/avatars.go create mode 100644 vendor/codeberg.org/Codeberg/avatars/go.mod create mode 100644 vendor/codeberg.org/Codeberg/avatars/mosaic.png create mode 100644 vendor/github.com/srwiley/oksvg/.gitignore create mode 100644 vendor/github.com/srwiley/oksvg/LICENSE create mode 100644 vendor/github.com/srwiley/oksvg/README.md create mode 100644 vendor/github.com/srwiley/oksvg/svgd.go create mode 100644 vendor/github.com/srwiley/oksvg/svgp.go create mode 100644 vendor/github.com/srwiley/rasterx/LICENSE create mode 100644 vendor/github.com/srwiley/rasterx/README.md create mode 100644 vendor/github.com/srwiley/rasterx/dash.go create mode 100644 vendor/github.com/srwiley/rasterx/fill.go create mode 100644 vendor/github.com/srwiley/rasterx/geomx.go create mode 100644 vendor/github.com/srwiley/rasterx/gradient.go create mode 100644 vendor/github.com/srwiley/rasterx/matrix.go create mode 100644 vendor/github.com/srwiley/rasterx/scan.go create mode 100644 vendor/github.com/srwiley/rasterx/shapes.go create mode 100644 vendor/github.com/srwiley/rasterx/stroke.go create mode 100644 vendor/golang.org/x/image/AUTHORS create mode 100644 vendor/golang.org/x/image/CONTRIBUTORS create mode 100644 vendor/golang.org/x/image/LICENSE create mode 100644 vendor/golang.org/x/image/PATENTS create mode 100644 vendor/golang.org/x/image/colornames/colornames.go create mode 100644 vendor/golang.org/x/image/colornames/table.go create mode 100644 vendor/golang.org/x/image/math/fixed/fixed.go create mode 100644 vendor/golang.org/x/image/vector/acc_amd64.go create mode 100644 vendor/golang.org/x/image/vector/acc_amd64.s create mode 100644 vendor/golang.org/x/image/vector/acc_other.go create mode 100644 vendor/golang.org/x/image/vector/gen_acc_amd64.s.tmpl create mode 100644 vendor/golang.org/x/image/vector/raster_fixed.go create mode 100644 vendor/golang.org/x/image/vector/raster_floating.go create mode 100644 vendor/golang.org/x/image/vector/vector.go diff --git a/go.mod b/go.mod index ae37a8240b87a..6a63d3cfad030 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( cloud.google.com/go v0.78.0 // indirect code.gitea.io/gitea-vet v0.2.1 code.gitea.io/sdk/gitea v0.14.0 + codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797 gitea.com/go-chi/binding v0.0.0-20211013065440-d16dc407c2be gitea.com/go-chi/cache v0.0.0-20211013020926-78790b11abf1 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 @@ -102,6 +103,8 @@ require ( github.com/sergi/go-diff v1.2.0 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 + github.com/srwiley/oksvg v0.0.0-20211104221756-aeb4ca2c1505 + github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 diff --git a/go.sum b/go.sum index 58b21910cb57c..aa391054472f0 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0= code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= +codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797 h1:B1Wf52i7Hd5jiNVXIHdk651FL1E2Zy1GNkkNpvvfS4A= +codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797/go.mod h1:ML/htpPRb3+owhkm4+qG2ZrXnk5WXaQLASOZ5GLCPi8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gitea.com/go-chi/binding v0.0.0-20211013065440-d16dc407c2be h1:IzSwPVzd2hE6e67ujY8ReBCrQ5IFNd0uiBmC7Ux5IaY= gitea.com/go-chi/binding v0.0.0-20211013065440-d16dc407c2be/go.mod h1:/vR0YjlusOYvosKYW7QKcSnrY0nPLe4RQ/DGi3+i/Do= @@ -1105,6 +1107,10 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/srwiley/oksvg v0.0.0-20211104221756-aeb4ca2c1505 h1:fWb9FJAdmiORr+NEeaadjEXp6C2qGAjdd7icfppTskE= +github.com/srwiley/oksvg v0.0.0-20211104221756-aeb4ca2c1505/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= +github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 h1:oDMiXaTMyBEuZMU53atpxqYsSB3U1CHkeAu2zr6wTeY= +github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= @@ -1278,6 +1284,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/vendor/codeberg.org/Codeberg/avatars/.gitignore b/vendor/codeberg.org/Codeberg/avatars/.gitignore new file mode 100644 index 0000000000000..28a6b03564dc6 --- /dev/null +++ b/vendor/codeberg.org/Codeberg/avatars/.gitignore @@ -0,0 +1,2 @@ +avatars +.idea diff --git a/vendor/codeberg.org/Codeberg/avatars/README.md b/vendor/codeberg.org/Codeberg/avatars/README.md new file mode 100644 index 0000000000000..3789b5b12ca41 --- /dev/null +++ b/vendor/codeberg.org/Codeberg/avatars/README.md @@ -0,0 +1,32 @@ +# Golang port of DiceBear Avatars + +which in turn were inspired by 8biticon avatars + +![Example images](mosaic.png) + +## Usage + +import `codeberg.org/Codeberg/avatars` & use `string svg = avatars.MakeAvatar(seed)` + +## Build & Use Example + +```shell +# will build the binary +go build -o avatars example/main.go +# Create a random avatar and write it to /tmp/avatar.svg. Note that for the same seed string always the same image will be generated. +./avatars my-special-seed-string > /tmp/avatar.svg +# Open SVG file with image viewer +eog /tmp/avatar.svg +``` + +## Create mosaic with example images using rsvg and imagemagick montage + +```shell +go build -o avatars example/main.go +for i in {1..40} ; do + ./avatars my-special-seed-string_$i > /tmp/avatar_$i.svg + rsvg-convert /tmp/avatar_$i.svg -z 10 -o /tmp/avatar_$i.png +done +montage -tile 10x4 /tmp/avatar_*.png /tmp/mosaic.png +eog /tmp/mosaic.png +``` diff --git a/vendor/codeberg.org/Codeberg/avatars/avatars.go b/vendor/codeberg.org/Codeberg/avatars/avatars.go new file mode 100644 index 0000000000000..71279e09f4332 --- /dev/null +++ b/vendor/codeberg.org/Codeberg/avatars/avatars.go @@ -0,0 +1,536 @@ +package avatars + +import ( + "fmt" + "math" + "math/bits" + "strconv" + "strings" +) + +// MakeAvatar create svg from seed +func MakeAvatar(seedString string) string { + var seed uint64 + for _, c := range []byte(seedString) { + seed = bits.RotateLeft64(seed, 8) + seed ^= uint64(c) + } + if seed&1 == 0 { + return femaleAvatar(seed, "") + } + return maleAvatar(seed, "") +} + +/** + * Based on TypeScript DiceBear Avatars, which in turn were inspired by 8biticon avatars: + * (MIT License, Copyright (c) 2012 Plastic Jam, Copyright (c) 2019 DiceBear) + * cf. https://github.com/DiceBear/avatars/blob/master/packages/avatars-male-sprites/src/index.ts + */ + +type lcg struct { + seed uint64 +} + +func (g *lcg) random() uint32 { + /* Linear Congruent Generator, POSIX/glibc [de]rand48 setting, bits [47..0] are output bits */ + g.seed = (25214903917*g.seed + 11) % 281474976710656 + return uint32(g.seed) +} + +func (g *lcg) binomial(p float64) bool { + /* Sample from Binomial distribution with probability p */ + var sample = float64(g.random()) * float64(1.0/4294967295.0) + return sample > p +} + +func (g *lcg) pickOne(s []string) string { + /* Pick one element from list */ + var N = uint32(len(s)) + return s[g.random()%N] +} + +func (g *lcg) pickOneFloat(s []float64) float64 { + /* Pick one element from list - float version*/ + var N = uint32(len(s)) + return s[g.random()%N] +} + +func (g *lcg) pickAorB(p float64, a string, b string) string { + /* Pick a or b with probability p of picking a */ + if g.binomial(p) { + return a + } + return b +} + +func linearCongruentialGenerator(seed uint64) *lcg { + g := new(lcg) + g.seed = seed + return g +} + +type rgb struct { + r uint8 + g uint8 + b uint8 + a uint8 +} + +type hsv struct { + h float64 + s float64 + v float64 +} + +func f2rgb(r, g, b float64) *rgb { + // make rgb from 3 floats + c := new(rgb) + c.r = uint8(r * 1.0 / 255) + c.g = uint8(g * 1.0 / 255) + c.b = uint8(b * 1.0 / 255) + c.a = 255 + return c +} + +func f2hsv(h, s, v float64) *hsv { + // make hsv from 3 floats + c := new(hsv) + c.h = h + c.s = s + c.v = v + return c +} + +func toRgb(s string) *rgb { + c := new(rgb) + c.a = 255 + fmt.Sscanf(s, "#%02x%02x%02x", &c.r, &c.g, &c.b) + return c +} + +func (c *rgb) toHsv() *hsv { + var h float64 + var s float64 + var v float64 + r := 255.0 * float64(c.r) + g := 255.0 * float64(c.g) + b := 255.0 * float64(c.b) + min := math.Min(math.Min(r, g), b) + v = math.Max(math.Max(r, g), b) + C := v - min + s = 0.0 + if v != 0.0 { + s = C / v + } + h = 0.0 + if min != v { + if v == r { + h = math.Mod((g-b)/C, 6.0) + } + if v == g { + h = (b-r)/C + 2.0 + } + if v == b { + h = (r-g)/C + 4.0 + } + h *= 60.0 + if h < 0.0 { + h += 360.0 + } + } + return f2hsv(h, s, v) +} + +func (c *hsv) toRgb() *rgb { + h := int((c.h / 60)) + f := c.h/60 - float64(h) + p := c.v * (1 - c.s) + q := c.v * (1 - c.s*f) + t := c.v * (1 - c.s*(1-f)) + switch h { + case 6: + case 0: + return f2rgb(c.v, t, p) + case 1: + return f2rgb(q, c.v, p) + case 2: + return f2rgb(p, c.v, t) + case 3: + return f2rgb(p, q, c.v) + case 4: + return f2rgb(t, p, c.v) + case 5: + return f2rgb(c.v, p, q) + } + return f2rgb(0, 0, 0) +} + +func (c *rgb) brighterThan(ref *rgb, delta float64) *rgb { + primary := c.toHsv() + secondary := ref.toHsv() + if primary.v >= secondary.v+delta { + return c + } + primary.v = secondary.v + delta + if primary.v > 360 { + primary.v = 360 + } + return primary.toRgb() +} + +func (c *rgb) darkerThan(ref *rgb, delta float64) *rgb { + primary := c.toHsv() + secondary := ref.toHsv() + if primary.v <= secondary.v-delta { + return c + } + primary.v = secondary.v - delta + if primary.v < 0 { + primary.v = 0 + } + return primary.toRgb() +} + +func (c *rgb) brighterOrDarkerThan(ref *rgb, delta float64) *rgb { + primary := c.toHsv() + secondary := ref.toHsv() + if primary.v <= secondary.v { + return c.darkerThan(ref, delta) + } + return c.brighterThan(ref, delta) +} + +func (c *rgb) withAlpha(alpha float64) *rgb { + c.a = uint8(alpha * 255) + return c +} + +func (c *rgb) html() string { + if c.a == 255 { + return fmt.Sprintf("#%02x%02x%02x", c.r, c.g, c.b) + } + return fmt.Sprintf("#%02x%02x%02x%02x", c.r, c.g, c.b, c.a) +} + +func maleAvatar(seed uint64, mood string) string { + var g = linearCongruentialGenerator(seed) + var skinColor = toRgb(g.pickOne([]string{"#FFDBAC", "#F5CFA0", "#EAC393", "#E0B687", "#CB9E6E", "#B68655", "#A26D3D", "#8D5524"})) + var hairColor = toRgb(g.pickOne([]string{"#090806", "#2c222b", "#71635a", "#b7a69e", "#b89778", "#a56b46", "#b55239", "#8d4a43", + "#91553d", "#533d32", "#3b3024", "#554838", "#4e433f", "#504444", "#6a4e42", "#a7856a", "#977961"})).brighterOrDarkerThan(skinColor, 17) + var eyesColor = toRgb(g.pickOne([]string{"#76778b", "#697b94", "#647b90", "#5b7c8b", "#588387"})) + var eyebrowsColor = hairColor.darkerThan(skinColor, 7).darkerThan(hairColor, 10) + var mustacheColor = hairColor.darkerThan(skinColor, 7).withAlpha(g.pickOneFloat([]float64{1, 0.75, 0.5})) + var mouthColor = toRgb(g.pickOne([]string{"#eec1ad", "#dbac98", "#d29985"})).brighterOrDarkerThan(skinColor, 10) + var glassesColor = toRgb(g.pickOne([]string{"#5f705c", "#43677d", "#5e172d", "#ffb67a", "#a04b5d", "#191919", "#323232", "#4b4b4b"})) + var clothesColor = toRgb(g.pickOne([]string{"#5bc0de", "#5cb85c", "#428bca", "#03396c", "#005b96", "#6497b1", "#1b85b8", "#5a5255", "#559e83", "#ae5a41", "#c3cb71", "#666547", "#ffe28a"})) + var hatColor = toRgb(g.pickOne([]string{"#18293b", "#2e1e05", "#989789", "#3d6ba7", "#517459", "#a62116"})) + + if mood == "" { + mood = g.pickOne([]string{"sad", "happy", "surprised"}) + } + var mouth string + if mood == "sad" { + mouth = "" + + "" + + "" + + "" + + "" + } else if mood == "happy" { + mouth = "" + + "" + + "" + + "" + } else if mood == "surprised" { + mouth = "" + + "" + } + + var s = strings.Join([]string{ + "", + // Head + "", + // Eyes + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Eyebrows + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Mustache (50% chance) + g.pickAorB(0.5, + g.pickOne([]string{ + "", + "", + "", + "", + }), + ""), + // Mouth + mouth, + // Glasses (25% chance) + g.pickAorB(0.25, + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + }), + ""), + // Clothes + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Hair (95% chance) + g.pickAorB(0.95, + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + ""), + // Hat (5% chance) + g.pickAorB(0.05, + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + ""), + "", + }, "") + + m := []string{ + "${skinColor}", skinColor.html(), + "${hairColor}", hairColor.html(), + "${eyesColor}", eyesColor.html(), + "${eyebrowsColor}", eyebrowsColor.html(), + "${mustacheColor}", mustacheColor.html(), + "${mustacheColorAlpha}", strconv.Itoa(int(mustacheColor.a)), + "${mouthColor}", mouthColor.html(), + "${glassesColor}", glassesColor.html(), + "${clothesColor}", clothesColor.html(), + "${hatColor}", hatColor.html(), + } + return strings.NewReplacer(m...).Replace(s) +} + +func femaleAvatar(seed uint64, mood string) string { + var g = linearCongruentialGenerator(seed) + + var skinColor = toRgb(g.pickOne([]string{"#FFDBAC", "#F5CFA0", "#EAC393", "#E0B687", "#CB9E6E", "#B68655", "#A26D3D", "#8D5524"})) + var hairColor = toRgb(g.pickOne([]string{"#090806", "#2c222b", "#71635a", "#b7a69e", "#d6c4c2", "#cabfb1", "#dcd0ba", "#fff5e1", + "#e6cea8", "#e5c8a8", "#debc99", "#b89778", "#a56b46", "#b55239", "#8d4a43", "#91553d", + "#533d32", "#3b3024", "#554838", "#4e433f", "#504444", "#6a4e42", "#a7856a", "#977961"})).brighterOrDarkerThan(skinColor, 17) + var eyesColor = toRgb(g.pickOne([]string{"#76778b", "#697b94", "#647b90", "#5b7c8b", "#588387"})) + var eyebrowsColor = hairColor.darkerThan(skinColor, 7).darkerThan(hairColor, 10) + var accessoriesColor = toRgb(g.pickOne([]string{"#daa520", "#ffd700", "#eee8aa", "#fafad2", "#d3d3d3", "#a9a9a9"})) + var mouthColor = toRgb(g.pickOne([]string{"#dbac98", "#d29985", "#c98276", "#e35d6a", "#e32153", "#de0f0d"})).brighterOrDarkerThan(skinColor, 10) + var glassesColor = toRgb(g.pickOne([]string{"#5f705c", "#43677d", "#5e172d", "#ffb67a", "#a04b5d", "#191919", "#323232", "#4b4b4b"})) + var clothesColor = toRgb(g.pickOne([]string{"#d11141", "#00b159", "#00aedb", "#f37735", "#ffc425", "#740001", "#ae0001", "#eeba30", + "#96ceb4", "#ffeead", "#ff6f69", "#ffcc5c", "#88d8b0"})) + var hatColor = toRgb(g.pickOne([]string{"#cc6192", "#2663a3", "#a62116", "#3d8a6b", "#614f8a"})) + + if mood == "" { + mood = g.pickOne([]string{"sad", "happy", "surprised"}) + } + var mouth string + if mood == "sad" { + mouth = "" + + "" + + "" + + "" + } else if mood == "happy" { + mouth = "" + + "" + + "" + + "" + + "" + + "" + + "" + } else if mood == "surprised" { + mouth = "" + + "" + } + + var s = strings.Join([]string{ + "", + // Head + "", + // Eyes + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Eyebrows + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Accessories (15% chance) + g.pickAorB(0.15, + g.pickOne([]string{ + "", + "", + "", + "", + }), + ""), + // Mouth + mouth, + // Glasses (25% chance) + g.pickAorB(0.25, + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + }), + ""), + // Clothes + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Hair + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + // Hat (5% chance) + g.pickAorB(0.05, + g.pickOne([]string{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }), + ""), + "", + }, "") + + m := []string{ + "${skinColor}", skinColor.html(), + "${hairColor}", hairColor.html(), + "${eyesColor}", eyesColor.html(), + "${eyebrowsColor}", eyebrowsColor.html(), + "${accessoriesColor}", accessoriesColor.html(), + "${mouthColor}", mouthColor.html(), + "${glassesColor}", glassesColor.html(), + "${clothesColor}", clothesColor.html(), + "${hatColor}", hatColor.html(), + } + return strings.NewReplacer(m...).Replace(s) +} diff --git a/vendor/codeberg.org/Codeberg/avatars/go.mod b/vendor/codeberg.org/Codeberg/avatars/go.mod new file mode 100644 index 0000000000000..8f39a4047d84e --- /dev/null +++ b/vendor/codeberg.org/Codeberg/avatars/go.mod @@ -0,0 +1,3 @@ +module codeberg.org/Codeberg/avatars + +go 1.14 diff --git a/vendor/codeberg.org/Codeberg/avatars/mosaic.png b/vendor/codeberg.org/Codeberg/avatars/mosaic.png new file mode 100644 index 0000000000000000000000000000000000000000..5468e539c9dbafd06b35257816b2e97743d3088c GIT binary patch literal 309176 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

9Ket46N^FpLmf2 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ʨ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 I{QbxO0W%#;F#rGn literal 0 HcmV?d00001 diff --git a/vendor/github.com/srwiley/oksvg/.gitignore b/vendor/github.com/srwiley/oksvg/.gitignore new file mode 100644 index 0000000000000..868f98c5b3598 --- /dev/null +++ b/vendor/github.com/srwiley/oksvg/.gitignore @@ -0,0 +1 @@ +/testdata/*.png diff --git a/vendor/github.com/srwiley/oksvg/LICENSE b/vendor/github.com/srwiley/oksvg/LICENSE new file mode 100644 index 0000000000000..ab2ed6873d253 --- /dev/null +++ b/vendor/github.com/srwiley/oksvg/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2018, Steven R Wiley +All rights reserved. + +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 the copyright holder 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 HOLDER 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. diff --git a/vendor/github.com/srwiley/oksvg/README.md b/vendor/github.com/srwiley/oksvg/README.md new file mode 100644 index 0000000000000..2e09042284d63 --- /dev/null +++ b/vendor/github.com/srwiley/oksvg/README.md @@ -0,0 +1,49 @@ +# oksvg +oksvg is a rasterizer for a partial implementation of the SVG2.0 specification in golang. + +Although many SVG elements will not be read by oksvg, it is good enough to faithfully produce thousands, but certainly not all, SVG icons available both for free and commercially. A list of valid and invalid elements is in the doc folder. + +oksvg uses the [rasterx](https://github.com/srwiley/rasterx) rasterizer package which implements full SVG2.0 path functions, including the newer 'arc' join-mode. + +![arcs and caps](doc/TestShapes.png) + +### Extra non-standard features. + +In addition to 'arc' as a valid join mode value, oksvg also allows 'arc-clip' which is the arc analog of miter-clip and some extra capping and gap values. It can also specify different capping functions for line starts and ends. + +#### Rasterizations of SVG to PNG from creative commons 3.0 sources. + +Example renderings of unedited open source SVG files by oksvg and rasterx are shown below. + +Thanks to [Freepik](http://www.freepik.com) from [Flaticon](https://www.flaticon.com/) +Licensed by [Creative Commons 3.0](http://creativecommons.org/licenses/by/3.0/) for the example icons shown below, and also used as test icons in the testdata folder. + +![Jupiter](doc/jupiter.png) + +![lander](doc/lander.png) + +![mountains](doc/mountains.png) + +![bus](doc/school-bus.png) + +### Non-standard library dependencies +oksvg requires the following imports which are not included in the go standard library: + +* golang.org/x/net/html/charset +* golang.org/x/image/colornames +* golang.org/x/image/math/fixed + +These can be included in your gopath by the following 'get' commands: + +* "go get golang.org/x/image/math/fixed" +* "go get golang.org/x/image/colornames" +* "go get golang.org/x/net/html/charset" + +oksvg also requires the user to get or clone into the workspace the rasterx package located here: + +* github.com/srwiley/rasterx + + + + + diff --git a/vendor/github.com/srwiley/oksvg/svgd.go b/vendor/github.com/srwiley/oksvg/svgd.go new file mode 100644 index 0000000000000..42aa1859845d5 --- /dev/null +++ b/vendor/github.com/srwiley/oksvg/svgd.go @@ -0,0 +1,1299 @@ +// Copyright 2017 The oksvg Authors. All rights reserved. +// +// created: 2/12/2017 by S.R.Wiley +// The oksvg package provides a partial implementation of the SVG 2.0 standard. +// It can perform all SVG2.0 path commands, including arc and miterclip. It also +// has some additional capabilities like arc-clip. Svgdraw does +// not implement all SVG features such as animation or markers, but it can draw +// the many of open source SVG icons correctly. See Readme for +// a list of features. + +package oksvg + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + + "golang.org/x/net/html/charset" + + "encoding/xml" + "errors" + "image/color" + "log" + "math" + + "github.com/srwiley/rasterx" + "golang.org/x/image/colornames" + "golang.org/x/image/math/fixed" +) + +type ( + // PathStyle holds the state of the SVG style + PathStyle struct { + FillOpacity, LineOpacity float64 + LineWidth, DashOffset, MiterLimit float64 + Dash []float64 + UseNonZeroWinding bool + fillerColor, linerColor interface{} // either color.Color or rasterx.Gradient + LineGap rasterx.GapFunc + LeadLineCap rasterx.CapFunc // This is used if different than LineCap + LineCap rasterx.CapFunc + LineJoin rasterx.JoinMode + mAdder rasterx.MatrixAdder // current transform + } + + // SvgPath binds a style to a path + SvgPath struct { + PathStyle + Path rasterx.Path + } + + // SvgIcon holds data from parsed SVGs + SvgIcon struct { + ViewBox struct{ X, Y, W, H float64 } + Titles []string // Title elements collect here + Descriptions []string // Description elements collect here + Grads map[string]*rasterx.Gradient + Defs map[string][]definition + SVGPaths []SvgPath + Transform rasterx.Matrix2D + classes map[string]styleAttribute + } + + // IconCursor is used while parsing SVG files + IconCursor struct { + PathCursor + icon *SvgIcon + StyleStack []PathStyle + grad *rasterx.Gradient + inTitleText, inDescText, inGrad, inDefs, inDefsStyle bool + currentDef []definition + } + + // definition is used to store what's given in a def tag + definition struct { + ID, Tag string + Attrs []xml.Attr + } + + // styleAttribute describes draw options, such as {"fill":"black"; "stroke":"white"} + styleAttribute = map[string]string +) + +// DefaultStyle sets the default PathStyle to fill black, winding rule, +// full opacity, no stroke, ButtCap line end and Bevel line connect. +var DefaultStyle = PathStyle{1.0, 1.0, 2.0, 0.0, 4.0, nil, true, + color.NRGBA{0x00, 0x00, 0x00, 0xff}, nil, + nil, nil, rasterx.ButtCap, rasterx.Bevel, rasterx.MatrixAdder{M: rasterx.Identity}} + +// Draw the compiled SVG icon into the GraphicContext. +// All elements should be contained by the Bounds rectangle of the SvgIcon. +func (s *SvgIcon) Draw(r *rasterx.Dasher, opacity float64) { + for _, svgp := range s.SVGPaths { + svgp.DrawTransformed(r, opacity, s.Transform) + } +} + +// SetTarget sets the Transform matrix to draw within the bounds of the rectangle arguments +func (s *SvgIcon) SetTarget(x, y, w, h float64) { + scaleW := w / s.ViewBox.W + scaleH := h / s.ViewBox.H + s.Transform = rasterx.Identity.Translate(x-s.ViewBox.X, y-s.ViewBox.Y).Scale(scaleW, scaleH) +} + +// Draw the compiled SvgPath into the Dasher. +func (svgp *SvgPath) Draw(r *rasterx.Dasher, opacity float64) { + svgp.DrawTransformed(r, opacity, rasterx.Identity) +} + +// DrawTransformed draws the compiled SvgPath into the Dasher while applying transform t. +func (svgp *SvgPath) DrawTransformed(r *rasterx.Dasher, opacity float64, t rasterx.Matrix2D) { + m := svgp.mAdder.M + svgp.mAdder.M = t.Mult(m) + defer func() { svgp.mAdder.M = m }() // Restore untransformed matrix + if svgp.fillerColor != nil { + r.Clear() + rf := &r.Filler + rf.SetWinding(svgp.UseNonZeroWinding) + svgp.mAdder.Adder = rf // This allows transformations to be applied + svgp.Path.AddTo(&svgp.mAdder) + + switch fillerColor := svgp.fillerColor.(type) { + case color.Color: + rf.SetColor(rasterx.ApplyOpacity(fillerColor, svgp.FillOpacity*opacity)) + case rasterx.Gradient: + if fillerColor.Units == rasterx.ObjectBoundingBox { + fRect := rf.Scanner.GetPathExtent() + mnx, mny := float64(fRect.Min.X)/64, float64(fRect.Min.Y)/64 + mxx, mxy := float64(fRect.Max.X)/64, float64(fRect.Max.Y)/64 + fillerColor.Bounds.X, fillerColor.Bounds.Y = mnx, mny + fillerColor.Bounds.W, fillerColor.Bounds.H = mxx-mnx, mxy-mny + } + rf.SetColor(fillerColor.GetColorFunction(svgp.FillOpacity * opacity)) + } + rf.Draw() + // default is true + rf.SetWinding(true) + } + if svgp.linerColor != nil { + r.Clear() + svgp.mAdder.Adder = r + lineGap := svgp.LineGap + if lineGap == nil { + lineGap = DefaultStyle.LineGap + } + lineCap := svgp.LineCap + if lineCap == nil { + lineCap = DefaultStyle.LineCap + } + leadLineCap := lineCap + if svgp.LeadLineCap != nil { + leadLineCap = svgp.LeadLineCap + } + r.SetStroke(fixed.Int26_6(svgp.LineWidth*64), + fixed.Int26_6(svgp.MiterLimit*64), leadLineCap, lineCap, + lineGap, svgp.LineJoin, svgp.Dash, svgp.DashOffset) + svgp.Path.AddTo(&svgp.mAdder) + switch linerColor := svgp.linerColor.(type) { + case color.Color: + r.SetColor(rasterx.ApplyOpacity(linerColor, svgp.LineOpacity*opacity)) + case rasterx.Gradient: + if linerColor.Units == rasterx.ObjectBoundingBox { + fRect := r.Scanner.GetPathExtent() + mnx, mny := float64(fRect.Min.X)/64, float64(fRect.Min.Y)/64 + mxx, mxy := float64(fRect.Max.X)/64, float64(fRect.Max.Y)/64 + linerColor.Bounds.X, linerColor.Bounds.Y = mnx, mny + linerColor.Bounds.W, linerColor.Bounds.H = mxx-mnx, mxy-mny + } + r.SetColor(linerColor.GetColorFunction(svgp.LineOpacity * opacity)) + } + r.Draw() + } +} + +// GetFillColor returns the fill color of the SvgPath if one is defined and otherwise returns colornames.Black +func (svgp *SvgPath) GetFillColor() color.Color { + return getColor(svgp.fillerColor) +} + +// GetLineColor returns the stroke color of the SvgPath if one is defined and otherwise returns colornames.Black +func (svgp *SvgPath) GetLineColor() color.Color { + return getColor(svgp.linerColor) +} + +// SetFillColor sets the fill color of the SvgPath +func (svgp *SvgPath) SetFillColor(clr color.Color) { + svgp.fillerColor = clr +} + +// SetLineColor sets the line color of the SvgPath +func (svgp *SvgPath) SetLineColor(clr color.Color) { + svgp.linerColor = clr +} + +// ParseSVGColorNum reads the SFG color string e.g. #FBD9BD +func ParseSVGColorNum(colorStr string) (r, g, b uint8, err error) { + colorStr = strings.TrimPrefix(colorStr, "#") + var t uint64 + if len(colorStr) != 6 { + // SVG specs say duplicate characters in case of 3 digit hex number + colorStr = string([]byte{colorStr[0], colorStr[0], + colorStr[1], colorStr[1], colorStr[2], colorStr[2]}) + } + for _, v := range []struct { + c *uint8 + s string + }{ + {&r, colorStr[0:2]}, + {&g, colorStr[2:4]}, + {&b, colorStr[4:6]}} { + t, err = strconv.ParseUint(v.s, 16, 8) + if err != nil { + return + } + *v.c = uint8(t) + } + return +} + +// ParseSVGColor parses an SVG color string in all forms +// including all SVG1.1 names, obtained from the colornames package +func ParseSVGColor(colorStr string) (color.Color, error) { + //_, _, _, a := curColor.RGBA() + v := strings.ToLower(colorStr) + if strings.HasPrefix(v, "url") { // We are not handling urls + // and gradients and stuff at this point + return color.NRGBA{0, 0, 0, 255}, nil + } + switch v { + case "none", "": + // nil signals that the function (fill or stroke) is off; + // not the same as black + return nil, nil + default: + cn, ok := colornames.Map[v] + if ok { + r, g, b, a := cn.RGBA() + return color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}, nil + } + } + cStr := strings.TrimPrefix(colorStr, "rgb(") + if cStr != colorStr { + cStr := strings.TrimSuffix(cStr, ")") + vals := strings.Split(cStr, ",") + if len(vals) != 3 { + return color.NRGBA{}, errParamMismatch + } + var cvals [3]uint8 + var err error + for i := range cvals { + cvals[i], err = parseColorValue(vals[i]) + if err != nil { + return nil, err + } + } + return color.NRGBA{cvals[0], cvals[1], cvals[2], 0xFF}, nil + } + + cStr = strings.TrimPrefix(colorStr, "hsl(") + if cStr != colorStr { + cStr := strings.TrimSuffix(cStr, ")") + vals := strings.Split(cStr, ",") + if len(vals) != 3 { + return color.NRGBA{}, errParamMismatch + } + + H, err := strconv.ParseInt(strings.TrimSpace(vals[0]), 10, 64) + if err != nil { + return color.NRGBA{}, fmt.Errorf("invalid hue in hsl: '%s' (%s)", vals[0], err) + } + + S, err := strconv.ParseFloat(strings.TrimSpace(vals[1][:len(vals[1])-1]), 64) + if err != nil { + return color.NRGBA{}, fmt.Errorf("invalid saturation in hsl: '%s' (%s)", vals[1], err) + } + S = S / 100 + + L, err := strconv.ParseFloat(strings.TrimSpace(vals[2][:len(vals[2])-1]), 64) + if err != nil { + return color.NRGBA{}, fmt.Errorf("invalid lightness in hsl: '%s' (%s)", vals[2], err) + } + L = L / 100 + + C := (1 - math.Abs((2*L)-1)) * S + X := C * (1 - math.Abs(math.Mod((float64(H)/60), 2)-1)) + m := L - C/2 + + var rp, gp, bp float64 + if H < 60 { + rp, gp, bp = float64(C), float64(X), float64(0) + } else if H < 120 { + rp, gp, bp = float64(X), float64(C), float64(0) + } else if H < 180 { + rp, gp, bp = float64(0), float64(C), float64(X) + } else if H < 240 { + rp, gp, bp = float64(0), float64(X), float64(C) + } else if H < 300 { + rp, gp, bp = float64(X), float64(0), float64(C) + } else { + rp, gp, bp = float64(C), float64(0), float64(X) + } + + r, g, b := math.Round((rp+m)*255), math.Round((gp+m)*255), math.Round((bp+m)*255) + if r > 255 { + r = 255 + } + if g > 255 { + g = 255 + } + if b > 255 { + b = 255 + } + + return color.NRGBA{ + uint8(r), + uint8(g), + uint8(b), + 0xFF, + }, nil + } + + if colorStr[0] == '#' { + r, g, b, err := ParseSVGColorNum(colorStr) + if err != nil { + return nil, err + } + return color.NRGBA{r, g, b, 0xFF}, nil + } + return nil, errParamMismatch +} + +func parseColorValue(v string) (uint8, error) { + if v[len(v)-1] == '%' { + n, err := strconv.Atoi(strings.TrimSpace(v[:len(v)-1])) + if err != nil { + return 0, err + } + return uint8(n * 0xFF / 100), nil + } + n, err := strconv.Atoi(strings.TrimSpace(v)) + if n > 255 { + n = 255 + } + return uint8(n), err +} + +func (c *IconCursor) readTransformAttr(m1 rasterx.Matrix2D, k string) (rasterx.Matrix2D, error) { + ln := len(c.points) + switch k { + case "rotate": + if ln == 1 { + m1 = m1.Rotate(c.points[0] * math.Pi / 180) + } else if ln == 3 { + m1 = m1.Translate(c.points[1], c.points[2]). + Rotate(c.points[0]*math.Pi/180). + Translate(-c.points[1], -c.points[2]) + } else { + return m1, errParamMismatch + } + case "translate": + if ln == 1 { + m1 = m1.Translate(c.points[0], 0) + } else if ln == 2 { + m1 = m1.Translate(c.points[0], c.points[1]) + } else { + return m1, errParamMismatch + } + case "skewx": + if ln == 1 { + m1 = m1.SkewX(c.points[0] * math.Pi / 180) + } else { + return m1, errParamMismatch + } + case "skewy": + if ln == 1 { + m1 = m1.SkewY(c.points[0] * math.Pi / 180) + } else { + return m1, errParamMismatch + } + case "scale": + if ln == 1 { + m1 = m1.Scale(c.points[0], 0) + } else if ln == 2 { + m1 = m1.Scale(c.points[0], c.points[1]) + } else { + return m1, errParamMismatch + } + case "matrix": + if ln == 6 { + m1 = m1.Mult(rasterx.Matrix2D{ + A: c.points[0], + B: c.points[1], + C: c.points[2], + D: c.points[3], + E: c.points[4], + F: c.points[5]}) + } else { + return m1, errParamMismatch + } + default: + return m1, errParamMismatch + } + return m1, nil +} + +func (c *IconCursor) parseTransform(v string) (rasterx.Matrix2D, error) { + ts := strings.Split(v, ")") + m1 := c.StyleStack[len(c.StyleStack)-1].mAdder.M + for _, t := range ts { + t = strings.TrimSpace(t) + if len(t) == 0 { + continue + } + d := strings.Split(t, "(") + if len(d) != 2 || len(d[1]) < 1 { + return m1, errParamMismatch // badly formed transformation + } + err := c.GetPoints(d[1]) + if err != nil { + return m1, err + } + m1, err = c.readTransformAttr(m1, strings.ToLower(strings.TrimSpace(d[0]))) + if err != nil { + return m1, err + } + } + return m1, nil +} + +func (c *IconCursor) readStyleAttr(curStyle *PathStyle, k, v string) error { + switch k { + case "fill": + gradient, ok := c.ReadGradURL(v, curStyle.fillerColor) + if ok { + curStyle.fillerColor = gradient + break + } + var err error + curStyle.fillerColor, err = ParseSVGColor(v) + return err + case "stroke": + gradient, ok := c.ReadGradURL(v, curStyle.linerColor) + if ok { + curStyle.linerColor = gradient + break + } + col, errc := ParseSVGColor(v) + if errc != nil { + return errc + } + if col != nil { + curStyle.linerColor = col.(color.NRGBA) + } else { + curStyle.linerColor = nil + } + case "stroke-linegap": + switch v { + case "flat": + curStyle.LineGap = rasterx.FlatGap + case "round": + curStyle.LineGap = rasterx.RoundGap + case "cubic": + curStyle.LineGap = rasterx.CubicGap + case "quadratic": + curStyle.LineGap = rasterx.QuadraticGap + } + case "stroke-leadlinecap": + switch v { + case "butt": + curStyle.LeadLineCap = rasterx.ButtCap + case "round": + curStyle.LeadLineCap = rasterx.RoundCap + case "square": + curStyle.LeadLineCap = rasterx.SquareCap + case "cubic": + curStyle.LeadLineCap = rasterx.CubicCap + case "quadratic": + curStyle.LeadLineCap = rasterx.QuadraticCap + } + case "stroke-linecap": + switch v { + case "butt": + curStyle.LineCap = rasterx.ButtCap + case "round": + curStyle.LineCap = rasterx.RoundCap + case "square": + curStyle.LineCap = rasterx.SquareCap + case "cubic": + curStyle.LineCap = rasterx.CubicCap + case "quadratic": + curStyle.LineCap = rasterx.QuadraticCap + } + case "stroke-linejoin": + switch v { + case "miter": + curStyle.LineJoin = rasterx.Miter + case "miter-clip": + curStyle.LineJoin = rasterx.MiterClip + case "arc-clip": + curStyle.LineJoin = rasterx.ArcClip + case "round": + curStyle.LineJoin = rasterx.Round + case "arc": + curStyle.LineJoin = rasterx.Arc + case "bevel": + curStyle.LineJoin = rasterx.Bevel + } + case "stroke-miterlimit": + mLimit, err := parseFloat(v, 64) + if err != nil { + return err + } + curStyle.MiterLimit = mLimit + case "stroke-width": + width, err := parseFloat(v, 64) + if err != nil { + return err + } + curStyle.LineWidth = width + case "stroke-dashoffset": + dashOffset, err := parseFloat(v, 64) + if err != nil { + return err + } + curStyle.DashOffset = dashOffset + case "stroke-dasharray": + if v != "none" { + dashes := splitOnCommaOrSpace(v) + dList := make([]float64, len(dashes)) + for i, dstr := range dashes { + d, err := parseFloat(strings.TrimSpace(dstr), 64) + if err != nil { + return err + } + dList[i] = d + } + curStyle.Dash = dList + break + } + case "opacity", "stroke-opacity", "fill-opacity": + op, err := parseFloat(v, 64) + if err != nil { + return err + } + if k != "stroke-opacity" { + curStyle.FillOpacity *= op + } + if k != "fill-opacity" { + curStyle.LineOpacity *= op + } + case "transform": + m, err := c.parseTransform(v) + if err != nil { + return err + } + curStyle.mAdder.M = m + } + return nil +} + +// PushStyle parses the style element, and push it on the style stack. Only color and opacity are supported +// for fill. Note that this parses both the contents of a style attribute plus +// direct fill and opacity attributes. +func (c *IconCursor) PushStyle(attrs []xml.Attr) error { + var pairs []string + className := "" + for _, attr := range attrs { + switch strings.ToLower(attr.Name.Local) { + case "style": + pairs = append(pairs, strings.Split(attr.Value, ";")...) + case "class": + className = attr.Value + default: + pairs = append(pairs, attr.Name.Local+":"+attr.Value) + } + } + // Make a copy of the top style + curStyle := c.StyleStack[len(c.StyleStack)-1] + for _, pair := range pairs { + kv := strings.Split(pair, ":") + if len(kv) >= 2 { + k := strings.ToLower(kv[0]) + k = strings.TrimSpace(k) + v := strings.TrimSpace(kv[1]) + err := c.readStyleAttr(&curStyle, k, v) + if err != nil { + return err + } + } + } + c.adaptClasses(&curStyle, className) + c.StyleStack = append(c.StyleStack, curStyle) // Push style onto stack + return nil +} + +// unitSuffixes are suffixes sometimes applied to the width and height attributes +// of the svg element. +var unitSuffixes = []string{"cm", "mm", "px", "pt"} + +// trimSuffixes removes unitSuffixes from any number that is not just numeric +func trimSuffixes(a string) (b string) { + if a == "" || (a[len(a)-1] >= '0' && a[len(a)-1] <= '9') { + return a + } + b = a + for _, v := range unitSuffixes { + b = strings.TrimSuffix(b, v) + } + return +} + +// parseFloat is a helper function that strips suffixes before passing to strconv.ParseFloat +func parseFloat(s string, bitSize int) (float64, error) { + val := trimSuffixes(s) + return strconv.ParseFloat(val, bitSize) +} + +// splitOnCommaOrSpace returns a list of strings after splitting the input on comma and space delimiters +func splitOnCommaOrSpace(s string) []string { + return strings.FieldsFunc(s, + func(r rune) bool { + return r == ',' || r == ' ' + }) +} + +func (c *IconCursor) readStartElement(se xml.StartElement) (err error) { + var skipDef bool + if se.Name.Local == "radialGradient" || se.Name.Local == "linearGradient" || c.inGrad { + skipDef = true + } + if c.inDefs && !skipDef { + ID := "" + for _, attr := range se.Attr { + if attr.Name.Local == "id" { + ID = attr.Value + } + } + if ID != "" && len(c.currentDef) > 0 { + c.icon.Defs[c.currentDef[0].ID] = c.currentDef + c.currentDef = make([]definition, 0) + } + c.currentDef = append(c.currentDef, definition{ + ID: ID, + Tag: se.Name.Local, + Attrs: se.Attr, + }) + return nil + } + df, ok := drawFuncs[se.Name.Local] + if !ok { + errStr := "Cannot process svg element " + se.Name.Local + if c.ErrorMode == StrictErrorMode { + return errors.New(errStr) + } else if c.ErrorMode == WarnErrorMode { + log.Println(errStr) + } + return nil + } + err = df(c, se.Attr) + + if len(c.Path) > 0 { + //The cursor parsed a path from the xml element + pathCopy := make(rasterx.Path, len(c.Path)) + copy(pathCopy, c.Path) + c.icon.SVGPaths = append(c.icon.SVGPaths, + SvgPath{c.StyleStack[len(c.StyleStack)-1], pathCopy}) + c.Path = c.Path[:0] + } + return +} + +func (c *IconCursor) adaptClasses(pathStyle *PathStyle, className string) { + if className == "" || len(c.icon.classes) == 0 { + return + } + for k, v := range c.icon.classes[className] { + c.readStyleAttr(pathStyle, k, v) + } +} + +// ReadIconStream reads the Icon from the given io.Reader +// This only supports a sub-set of SVG, but +// is enough to draw many icons. If errMode is provided, +// the first value determines if the icon ignores, errors out, or logs a warning +// if it does not handle an element found in the icon file. Ignore warnings is +// the default if no ErrorMode value is provided. +func ReadIconStream(stream io.Reader, errMode ...ErrorMode) (*SvgIcon, error) { + icon := &SvgIcon{Defs: make(map[string][]definition), Grads: make(map[string]*rasterx.Gradient), Transform: rasterx.Identity} + cursor := &IconCursor{StyleStack: []PathStyle{DefaultStyle}, icon: icon} + if len(errMode) > 0 { + cursor.ErrorMode = errMode[0] + } + classInfo := "" + decoder := xml.NewDecoder(stream) + decoder.CharsetReader = charset.NewReaderLabel + for { + t, err := decoder.Token() + if err != nil { + if err == io.EOF { + break + } + return icon, err + } + // Inspect the type of the XML token + switch se := t.(type) { + case xml.StartElement: + // Reads all recognized style attributes from the start element + // and places it on top of the styleStack + err = cursor.PushStyle(se.Attr) + if err != nil { + return icon, err + } + err = cursor.readStartElement(se) + if err != nil { + return icon, err + } + if se.Name.Local == "style" && cursor.inDefs { + cursor.inDefsStyle = true + } + case xml.EndElement: + // pop style + cursor.StyleStack = cursor.StyleStack[:len(cursor.StyleStack)-1] + switch se.Name.Local { + case "g": + if cursor.inDefs { + cursor.currentDef = append(cursor.currentDef, definition{ + Tag: "endg", + }) + } + case "title": + cursor.inTitleText = false + case "desc": + cursor.inDescText = false + case "defs": + if len(cursor.currentDef) > 0 { + cursor.icon.Defs[cursor.currentDef[0].ID] = cursor.currentDef + cursor.currentDef = make([]definition, 0) + } + cursor.inDefs = false + case "radialGradient", "linearGradient": + cursor.inGrad = false + + case "style": + if cursor.inDefsStyle { + icon.classes, err = parseClasses(classInfo) + if err != nil { + return icon, err + } + cursor.inDefsStyle = false + } + } + case xml.CharData: + if cursor.inTitleText { + icon.Titles[len(icon.Titles)-1] += string(se) + } + if cursor.inDescText { + icon.Descriptions[len(icon.Descriptions)-1] += string(se) + } + if cursor.inDefsStyle { + classInfo = string(se) + } + } + } + return icon, nil +} + +func parseClasses(data string) (map[string]styleAttribute, error) { + res := map[string]styleAttribute{} + arr := strings.Split(data, "}") + for _, v := range arr { + v = strings.TrimSpace(v) + if v == "" { + continue + } + valueIndex := strings.Index(v, "{") + if valueIndex == -1 || valueIndex == len(v)-1 { + return res, errors.New(v + "}: invalid map format in class definitions") + } + classesStr := v[:valueIndex] + attrStr := v[valueIndex+1:] + attrMap, err := parseAttrs(attrStr) + if err != nil { + return res, err + } + classes := strings.Split(classesStr, ",") + for _, class := range classes { + class = strings.TrimSpace(class) + if len(class) > 0 && class[0] == '.' { + class = class[1:] + } + for attrKey, attrVal := range attrMap { + if res[class] == nil { + res[class] = make(styleAttribute, len(attrMap)) + } + res[class][attrKey] = attrVal + } + } + } + return res, nil +} + +func parseAttrs(attrStr string) (styleAttribute, error) { + arr := strings.Split(attrStr, ";") + res := make(styleAttribute, len(arr)) + for _, kv := range arr { + kv = strings.TrimSpace(kv) + if kv == "" { + continue + } + tmp := strings.SplitN(kv, ":", 2) + if len(tmp) != 2 { + return res, errors.New(kv + ": invalid attribute format") + } + k := strings.TrimSpace(tmp[0]) + v := strings.TrimSpace(tmp[1]) + res[k] = v + } + return res, nil +} + +// ReadIcon reads the Icon from the named file +// This only supports a sub-set of SVG, but +// is enough to draw many icons. If errMode is provided, +// the first value determines if the icon ignores, errors out, or logs a warning +// if it does not handle an element found in the icon file. Ignore warnings is +// the default if no ErrorMode value is provided. +func ReadIcon(iconFile string, errMode ...ErrorMode) (*SvgIcon, error) { + fin, errf := os.Open(iconFile) + if errf != nil { + return nil, errf + } + defer fin.Close() + return ReadIconStream(fin, errMode...) +} + +func readFraction(v string) (f float64, err error) { + v = strings.TrimSpace(v) + d := 1.0 + if strings.HasSuffix(v, "%") { + d = 100 + v = strings.TrimSuffix(v, "%") + } + f, err = parseFloat(v, 64) + f /= d + // Is this is an unnecessary restriction? For now fractions can be all values not just in the range [0,1] + // if f > 1 { + // f = 1 + // } else if f < 0 { + // f = 0 + // } + return +} + +// getColor is a helper function to get the background color +// if ReadGradUrl needs it. +func getColor(clr interface{}) color.Color { + switch c := clr.(type) { + case rasterx.Gradient: // This is a bit lazy but oh well + for _, s := range c.Stops { + if s.StopColor != nil { + return s.StopColor + } + } + case color.NRGBA: + return c + } + return colornames.Black +} + +func localizeGradIfStopClrNil(g *rasterx.Gradient, defaultColor interface{}) (grad rasterx.Gradient) { + grad = *g + for _, s := range grad.Stops { + if s.StopColor == nil { // This means we need copy the gradient's Stop slice + // and fill in the default color + + // Copy the stops + stops := make([]rasterx.GradStop, len(grad.Stops)) + copy(stops, grad.Stops) + grad.Stops = stops + // Use the background color when a stop color is nil + clr := getColor(defaultColor) + for i, s := range stops { + if s.StopColor == nil { + grad.Stops[i].StopColor = clr + } + } + break // Only need to do this once + } + } + return +} + +// ReadGradURL reads an SVG format gradient url +// Since the context of the gradient can affect the colors +// the current fill or line color is passed in and used in +// the case of a nil stopClor value +func (c *IconCursor) ReadGradURL(v string, defaultColor interface{}) (grad rasterx.Gradient, ok bool) { + if strings.HasPrefix(v, "url(") && strings.HasSuffix(v, ")") { + urlStr := strings.TrimSpace(v[4 : len(v)-1]) + if strings.HasPrefix(urlStr, "#") { + var g *rasterx.Gradient + g, ok = c.icon.Grads[urlStr[1:]] + if ok { + grad = localizeGradIfStopClrNil(g, defaultColor) + } + } + } + return +} + +// ReadGradAttr reads an SVG gradient attribute +func (c *IconCursor) ReadGradAttr(attr xml.Attr) (err error) { + switch attr.Name.Local { + case "gradientTransform": + c.grad.Matrix, err = c.parseTransform(attr.Value) + case "gradientUnits": + switch strings.TrimSpace(attr.Value) { + case "userSpaceOnUse": + c.grad.Units = rasterx.UserSpaceOnUse + case "objectBoundingBox": + c.grad.Units = rasterx.ObjectBoundingBox + } + case "spreadMethod": + switch strings.TrimSpace(attr.Value) { + case "pad": + c.grad.Spread = rasterx.PadSpread + case "reflect": + c.grad.Spread = rasterx.ReflectSpread + case "repeat": + c.grad.Spread = rasterx.RepeatSpread + } + } + return +} + +type svgFunc func(c *IconCursor, attrs []xml.Attr) error + +var ( + drawFuncs = map[string]svgFunc{ + "svg": svgF, + "g": gF, + "line": lineF, + "stop": stopF, + "rect": rectF, + "circle": circleF, + "ellipse": circleF, //circleF handles ellipse also + "polyline": polylineF, + "polygon": polygonF, + "path": pathF, + "desc": descF, + "defs": defsF, + "title": titleF, + "linearGradient": linearGradientF, + "radialGradient": radialGradientF, + } + + svgF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + c.icon.ViewBox.X = 0 + c.icon.ViewBox.Y = 0 + c.icon.ViewBox.W = 0 + c.icon.ViewBox.H = 0 + var width, height float64 + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "viewBox": + err = c.GetPoints(attr.Value) + if len(c.points) != 4 { + return errParamMismatch + } + c.icon.ViewBox.X = c.points[0] + c.icon.ViewBox.Y = c.points[1] + c.icon.ViewBox.W = c.points[2] + c.icon.ViewBox.H = c.points[3] + case "width": + width, err = parseFloat(attr.Value, 64) + case "height": + height, err = parseFloat(attr.Value, 64) + } + if err != nil { + return err + } + } + if c.icon.ViewBox.W == 0 { + c.icon.ViewBox.W = width + } + if c.icon.ViewBox.H == 0 { + c.icon.ViewBox.H = height + } + return nil + } + gF svgFunc = func(*IconCursor, []xml.Attr) error { return nil } // g does nothing but push the style + rectF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var x, y, w, h, rx, ry float64 + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "x": + x, err = parseFloat(attr.Value, 64) + case "y": + y, err = parseFloat(attr.Value, 64) + case "width": + w, err = parseFloat(attr.Value, 64) + case "height": + h, err = parseFloat(attr.Value, 64) + case "rx": + rx, err = parseFloat(attr.Value, 64) + case "ry": + ry, err = parseFloat(attr.Value, 64) + } + if err != nil { + return err + } + } + if w == 0 || h == 0 { + return nil + } + rasterx.AddRoundRect(x+c.curX, y+c.curY, w+x+c.curX, h+y+c.curY, rx, ry, 0, rasterx.RoundGap, &c.Path) + return nil + } + circleF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var cx, cy, rx, ry float64 + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "cx": + cx, err = parseFloat(attr.Value, 64) + case "cy": + cy, err = parseFloat(attr.Value, 64) + case "r": + rx, err = parseFloat(attr.Value, 64) + ry = rx + case "rx": + rx, err = parseFloat(attr.Value, 64) + case "ry": + ry, err = parseFloat(attr.Value, 64) + } + if err != nil { + return err + } + } + if rx == 0 || ry == 0 { // not drawn, but not an error + return nil + } + c.EllipseAt(cx+c.curX, cy+c.curY, rx, ry) + return nil + } + lineF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var x1, x2, y1, y2 float64 + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "x1": + x1, err = parseFloat(attr.Value, 64) + case "x2": + x2, err = parseFloat(attr.Value, 64) + case "y1": + y1, err = parseFloat(attr.Value, 64) + case "y2": + y2, err = parseFloat(attr.Value, 64) + } + if err != nil { + return err + } + } + c.Path.Start(fixed.Point26_6{ + X: fixed.Int26_6((x1 + c.curX) * 64), + Y: fixed.Int26_6((y1 + c.curY) * 64)}) + c.Path.Line(fixed.Point26_6{ + X: fixed.Int26_6((x2 + c.curX) * 64), + Y: fixed.Int26_6((y2 + c.curY) * 64)}) + return nil + } + polylineF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "points": + err = c.GetPoints(attr.Value) + if len(c.points)%2 != 0 { + return errors.New("polygon has odd number of points") + } + } + if err != nil { + return err + } + } + if len(c.points) > 4 { + c.Path.Start(fixed.Point26_6{ + X: fixed.Int26_6((c.points[0] + c.curX) * 64), + Y: fixed.Int26_6((c.points[1] + c.curY) * 64)}) + for i := 2; i < len(c.points)-1; i += 2 { + c.Path.Line(fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}) + } + } + return nil + } + polygonF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + err := polylineF(c, attrs) + if len(c.points) > 4 { + c.Path.Stop(true) + } + return err + } + pathF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "d": + err = c.CompilePath(attr.Value) + } + if err != nil { + return err + } + } + return nil + } + descF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + c.inDescText = true + c.icon.Descriptions = append(c.icon.Descriptions, "") + return nil + } + titleF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + c.inTitleText = true + c.icon.Titles = append(c.icon.Titles, "") + return nil + } + defsF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + c.inDefs = true + return nil + } + linearGradientF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var err error + c.inGrad = true + c.grad = &rasterx.Gradient{Points: [5]float64{0, 0, 1, 0, 0}, + IsRadial: false, Bounds: c.icon.ViewBox, Matrix: rasterx.Identity} + for _, attr := range attrs { + switch attr.Name.Local { + case "id": + id := attr.Value + if len(id) >= 0 { + c.icon.Grads[id] = c.grad + } else { + return errZeroLengthID + } + case "x1": + c.grad.Points[0], err = readFraction(attr.Value) + case "y1": + c.grad.Points[1], err = readFraction(attr.Value) + case "x2": + c.grad.Points[2], err = readFraction(attr.Value) + case "y2": + c.grad.Points[3], err = readFraction(attr.Value) + default: + err = c.ReadGradAttr(attr) + } + if err != nil { + return err + } + } + return nil + } + radialGradientF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + c.inGrad = true + c.grad = &rasterx.Gradient{Points: [5]float64{0.5, 0.5, 0.5, 0.5, 0.5}, + IsRadial: true, Bounds: c.icon.ViewBox, Matrix: rasterx.Identity} + var setFx, setFy bool + var err error + for _, attr := range attrs { + switch attr.Name.Local { + case "id": + id := attr.Value + if len(id) >= 0 { + c.icon.Grads[id] = c.grad + } else { + return errZeroLengthID + } + case "r": + c.grad.Points[4], err = readFraction(attr.Value) + case "cx": + c.grad.Points[0], err = readFraction(attr.Value) + case "cy": + c.grad.Points[1], err = readFraction(attr.Value) + case "fx": + setFx = true + c.grad.Points[2], err = readFraction(attr.Value) + case "fy": + setFy = true + c.grad.Points[3], err = readFraction(attr.Value) + default: + err = c.ReadGradAttr(attr) + } + if err != nil { + return err + } + } + if !setFx { // set fx to cx by default + c.grad.Points[2] = c.grad.Points[0] + } + if !setFy { // set fy to cy by default + c.grad.Points[3] = c.grad.Points[1] + } + return nil + } + stopF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var err error + if c.inGrad { + stop := rasterx.GradStop{Opacity: 1.0} + for _, attr := range attrs { + switch attr.Name.Local { + case "offset": + stop.Offset, err = readFraction(attr.Value) + case "stop-color": + //todo: add current color inherit + stop.StopColor, err = ParseSVGColor(attr.Value) + case "stop-opacity": + stop.Opacity, err = parseFloat(attr.Value, 64) + } + if err != nil { + return err + } + } + c.grad.Stops = append(c.grad.Stops, stop) + } + return nil + } + useF svgFunc = func(c *IconCursor, attrs []xml.Attr) error { + var ( + href string + x, y float64 + err error + ) + for _, attr := range attrs { + switch attr.Name.Local { + case "href": + href = attr.Value + case "x": + x, err = parseFloat(attr.Value, 64) + case "y": + y, err = parseFloat(attr.Value, 64) + } + if err != nil { + return err + } + } + c.curX, c.curY = x, y + defer func() { + c.curX, c.curY = 0, 0 + }() + if href == "" { + return errors.New("only use tags with href is supported") + } + if !strings.HasPrefix(href, "#") { + return errors.New("only the ID CSS selector is supported") + } + defs, ok := c.icon.Defs[href[1:]] + if !ok { + return errors.New("href ID in use statement was not found in saved defs") + } + for _, def := range defs { + if def.Tag == "endg" { + // pop style + c.StyleStack = c.StyleStack[:len(c.StyleStack)-1] + continue + } + if err = c.PushStyle(def.Attrs); err != nil { + return err + } + df, ok := drawFuncs[def.Tag] + if !ok { + errStr := "Cannot process svg element " + def.Tag + if c.ErrorMode == StrictErrorMode { + return errors.New(errStr) + } else if c.ErrorMode == WarnErrorMode { + log.Println(errStr) + } + return nil + } + if err := df(c, def.Attrs); err != nil { + return err + } + if def.Tag != "g" { + // pop style + c.StyleStack = c.StyleStack[:len(c.StyleStack)-1] + } + } + return nil + } +) + +func init() { + // avoids cyclical static declaration + // called on package initialization + drawFuncs["use"] = useF +} diff --git a/vendor/github.com/srwiley/oksvg/svgp.go b/vendor/github.com/srwiley/oksvg/svgp.go new file mode 100644 index 0000000000000..b109607e5c12e --- /dev/null +++ b/vendor/github.com/srwiley/oksvg/svgp.go @@ -0,0 +1,396 @@ +// Copyright 2017 The oksvg Authors. All rights reserved. +// created: 2/12/2017 by S.R.Wiley +// +// svgd.go implements translation of an SVG2.0 path into a rasterx Path. + +package oksvg + +import ( + "errors" + "log" + "math" + "unicode" + + "github.com/srwiley/rasterx" + + "golang.org/x/image/math/fixed" +) + +type ( + //ErrorMode is the for setting how the parser reacts to unparsed elements + ErrorMode uint8 + // PathCursor is used to parse SVG format path strings into a rasterx Path + PathCursor struct { + rasterx.Path + placeX, placeY float64 + curX, curY float64 + cntlPtX, cntlPtY float64 + pathStartX, pathStartY float64 + points []float64 + lastKey uint8 + ErrorMode ErrorMode + inPath bool + } +) + +var ( + errParamMismatch = errors.New("param mismatch") + errCommandUnknown = errors.New("unknown command") + errZeroLengthID = errors.New("zero length id") +) + +const ( + //IgnoreErrorMode skips unparsed SVG elements + IgnoreErrorMode ErrorMode = iota + //WarnErrorMode outputs a warning when an unparsed SVG element is found + WarnErrorMode + //StrictErrorMode causes a error when an unparsed SVG element is found + StrictErrorMode +) + +func reflect(px, py, rx, ry float64) (x, y float64) { + return px*2 - rx, py*2 - ry +} + +func (c *PathCursor) valsToAbs(last float64) { + for i := 0; i < len(c.points); i++ { + last += c.points[i] + c.points[i] = last + } +} + +func (c *PathCursor) pointsToAbs(sz int) { + lastX := c.placeX + lastY := c.placeY + for j := 0; j < len(c.points); j += sz { + for i := 0; i < sz; i += 2 { + c.points[i+j] += lastX + c.points[i+1+j] += lastY + } + lastX = c.points[(j+sz)-2] + lastY = c.points[(j+sz)-1] + } +} + +func (c *PathCursor) hasSetsOrMore(sz int, rel bool) bool { + if !(len(c.points) >= sz && len(c.points)%sz == 0) { + return false + } + if rel { + c.pointsToAbs(sz) + } + return true +} + +// ReadFloat reads a floating point value and adds it to the cursor's points slice. +func (c *PathCursor) ReadFloat(numStr string) error { + last := 0 + isFirst := true + for i, n := range numStr { + if n == '.' { + if isFirst { + isFirst = false + continue + } + f, err := parseFloat(numStr[last:i], 64) + if err != nil { + return err + } + c.points = append(c.points, f) + last = i + } + } + f, err := parseFloat(numStr[last:], 64) + if err != nil { + return err + } + c.points = append(c.points, f) + return nil +} + +// GetPoints reads a set of floating point values from the SVG format number string, +// and add them to the cursor's points slice. +func (c *PathCursor) GetPoints(dataPoints string) error { + lastIndex := -1 + c.points = c.points[0:0] + lr := ' ' + for i, r := range dataPoints { + if !unicode.IsNumber(r) && r != '.' && !(r == '-' && lr == 'e') && r != 'e' { + if lastIndex != -1 { + if err := c.ReadFloat(dataPoints[lastIndex:i]); err != nil { + return err + } + } + if r == '-' { + lastIndex = i + } else { + lastIndex = -1 + } + } else if lastIndex == -1 { + lastIndex = i + } + lr = r + } + if lastIndex != -1 && lastIndex != len(dataPoints) { + if err := c.ReadFloat(dataPoints[lastIndex:]); err != nil { + return err + } + } + return nil +} + +func (c *PathCursor) reflectControlQuad() { + switch c.lastKey { + case 'q', 'Q', 'T', 't': + c.cntlPtX, c.cntlPtY = reflect(c.placeX, c.placeY, c.cntlPtX, c.cntlPtY) + default: + c.cntlPtX, c.cntlPtY = c.placeX, c.placeY + } +} + +func (c *PathCursor) reflectControlCube() { + switch c.lastKey { + case 'c', 'C', 's', 'S': + c.cntlPtX, c.cntlPtY = reflect(c.placeX, c.placeY, c.cntlPtX, c.cntlPtY) + default: + c.cntlPtX, c.cntlPtY = c.placeX, c.placeY + } +} + +// addSeg decodes an SVG seqment string into equivalent raster path commands saved +// in the cursor's Path +func (c *PathCursor) addSeg(segString string) error { + // Parse the string describing the numeric points in SVG format + if err := c.GetPoints(segString[1:]); err != nil { + return err + } + l := len(c.points) + k := segString[0] + rel := false + switch k { + case 'z': + fallthrough + case 'Z': + if len(c.points) != 0 { + return errParamMismatch + } + if c.inPath { + c.Path.Stop(true) + c.placeX = c.pathStartX + c.placeY = c.pathStartY + c.inPath = false + } + case 'm': + rel = true + fallthrough + case 'M': + if !c.hasSetsOrMore(2, rel) { + return errParamMismatch + } + c.pathStartX, c.pathStartY = c.points[0], c.points[1] + c.inPath = true + c.Path.Start(fixed.Point26_6{X: fixed.Int26_6((c.pathStartX + c.curX) * 64), Y: fixed.Int26_6((c.pathStartY + c.curY) * 64)}) + for i := 2; i < l-1; i += 2 { + c.Path.Line(fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}) + } + c.placeX = c.points[l-2] + c.placeY = c.points[l-1] + case 'l': + rel = true + fallthrough + case 'L': + if !c.hasSetsOrMore(2, rel) { + return errParamMismatch + } + for i := 0; i < l-1; i += 2 { + c.Path.Line(fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}) + } + c.placeX = c.points[l-2] + c.placeY = c.points[l-1] + case 'v': + c.valsToAbs(c.placeY) + fallthrough + case 'V': + if !c.hasSetsOrMore(1, false) { + return errParamMismatch + } + for _, p := range c.points { + c.Path.Line(fixed.Point26_6{ + X: fixed.Int26_6((c.placeX + c.curX) * 64), + Y: fixed.Int26_6((p + c.curY) * 64)}) + } + c.placeY = c.points[l-1] + case 'h': + c.valsToAbs(c.placeX) + fallthrough + case 'H': + if !c.hasSetsOrMore(1, false) { + return errParamMismatch + } + for _, p := range c.points { + c.Path.Line(fixed.Point26_6{ + X: fixed.Int26_6((p + c.curX) * 64), + Y: fixed.Int26_6((c.placeY + c.curY) * 64)}) + } + c.placeX = c.points[l-1] + case 'q': + rel = true + fallthrough + case 'Q': + if !c.hasSetsOrMore(4, rel) { + return errParamMismatch + } + for i := 0; i < l-3; i += 4 { + c.Path.QuadBezier( + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}, + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i+2] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+3] + c.curY) * 64)}) + } + c.cntlPtX, c.cntlPtY = c.points[l-4], c.points[l-3] + c.placeX = c.points[l-2] + c.placeY = c.points[l-1] + case 't': + rel = true + fallthrough + case 'T': + if !c.hasSetsOrMore(2, rel) { + return errParamMismatch + } + for i := 0; i < l-1; i += 2 { + c.reflectControlQuad() + c.Path.QuadBezier( + fixed.Point26_6{ + X: fixed.Int26_6((c.cntlPtX + c.curX) * 64), + Y: fixed.Int26_6((c.cntlPtY + c.curY) * 64)}, + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}) + c.lastKey = k + c.placeX = c.points[i] + c.placeY = c.points[i+1] + } + case 'c': + rel = true + fallthrough + case 'C': + if !c.hasSetsOrMore(6, rel) { + return errParamMismatch + } + for i := 0; i < l-5; i += 6 { + c.Path.CubeBezier( + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}, + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i+2] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+3] + c.curY) * 64)}, + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i+4] + c.curX) * 64), + Y: fixed.Int26_6((c.points[i+5] + c.curY) * 64)}) + } + c.cntlPtX, c.cntlPtY = c.points[l-4], c.points[l-3] + c.placeX = c.points[l-2] + c.placeY = c.points[l-1] + case 's': + rel = true + fallthrough + case 'S': + if !c.hasSetsOrMore(4, rel) { + return errParamMismatch + } + for i := 0; i < l-3; i += 4 { + c.reflectControlCube() + c.Path.CubeBezier(fixed.Point26_6{ + X: fixed.Int26_6((c.cntlPtX + c.curX) * 64), Y: fixed.Int26_6((c.cntlPtY + c.curY) * 64)}, + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i] + c.curX) * 64), Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)}, + fixed.Point26_6{ + X: fixed.Int26_6((c.points[i+2] + c.curX) * 64), Y: fixed.Int26_6((c.points[i+3] + c.curY) * 64)}) + c.lastKey = k + c.cntlPtX, c.cntlPtY = c.points[i], c.points[i+1] + c.placeX = c.points[i+2] + c.placeY = c.points[i+3] + } + case 'a', 'A': + if !c.hasSetsOrMore(7, false) { + return errParamMismatch + } + for i := 0; i < l-6; i += 7 { + if k == 'a' { + c.points[i+5] += c.placeX + c.points[i+6] += c.placeY + } + c.AddArcFromA(c.points[i:]) + } + default: + if c.ErrorMode == StrictErrorMode { + return errCommandUnknown + } + if c.ErrorMode == WarnErrorMode { + log.Println("Ignoring svg command " + string(k)) + } + } + // So we know how to extend some segment types + c.lastKey = k + return nil +} + +//EllipseAt adds a path of an elipse centered at cx, cy of radius rx and ry +// to the PathCursor +func (c *PathCursor) EllipseAt(cx, cy, rx, ry float64) { + c.placeX, c.placeY = cx+rx, cy + c.points = c.points[0:0] + c.points = append(c.points, rx, ry, 0.0, 1.0, 0.0, c.placeX, c.placeY) + c.Path.Start(fixed.Point26_6{ + X: fixed.Int26_6(c.placeX * 64), + Y: fixed.Int26_6(c.placeY * 64)}) + c.placeX, c.placeY = rasterx.AddArc(c.points, cx, cy, c.placeX, c.placeY, &c.Path) + c.Path.Stop(true) +} + +//AddArcFromA adds a path of an arc element to the cursor path to the PathCursor +func (c *PathCursor) AddArcFromA(points []float64) { + cx, cy := rasterx.FindEllipseCenter(&points[0], &points[1], points[2]*math.Pi/180, c.placeX, + c.placeY, points[5], points[6], points[4] == 0, points[3] == 0) + c.placeX, c.placeY = rasterx.AddArc(c.points, cx+c.curX, cy+c.curY, c.placeX+c.curX, c.placeY+c.curY, &c.Path) +} + +func (c *PathCursor) init() { + c.placeX = 0.0 + c.placeY = 0.0 + c.points = c.points[0:0] + c.lastKey = ' ' + c.Path.Clear() + c.inPath = false +} + +// CompilePath translates the svgPath description string into a rasterx path. +// All valid SVG path elements are interpreted to rasterx equivalents. +// The resulting path element is stored in the PathCursor. +func (c *PathCursor) CompilePath(svgPath string) error { + c.init() + lastIndex := -1 + for i, v := range svgPath { + if unicode.IsLetter(v) && v != 'e' { + if lastIndex != -1 { + if err := c.addSeg(svgPath[lastIndex:i]); err != nil { + return err + } + } + lastIndex = i + } + } + if lastIndex != -1 { + if err := c.addSeg(svgPath[lastIndex:]); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/srwiley/rasterx/LICENSE b/vendor/github.com/srwiley/rasterx/LICENSE new file mode 100644 index 0000000000000..ab2ed6873d253 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2018, Steven R Wiley +All rights reserved. + +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 the copyright holder 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 HOLDER 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. diff --git a/vendor/github.com/srwiley/rasterx/README.md b/vendor/github.com/srwiley/rasterx/README.md new file mode 100644 index 0000000000000..e9dbfcda5e82d --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/README.md @@ -0,0 +1,84 @@ +# rasterx + +Rasterx is a golang rasterizer that implements path stroking functions capable of SVG 2.0 compliant 'arc' joins and explicit loop closing. + + + +* Paths can be explicity closed or left open, resulting in a line join or end caps. +* Arc joins are supported, which causes the extending edge from a Bezier curve to follow the radius of curvature at the end point rather than a straight line miter, resulting in a more fliud looking join. +* Not specified in the SVG2.0 spec., but supported in rasterx is the arc-clip join, which is the arc join analog of a miter-clip join, both of which end the miter at a specified distance, rather than all or nothing. +* Several cap and gap functions in addition to those specified by SVG2.0 are implemented, specifically quad and cubic caps and gaps. +* Line start and end capping functions can be different. + + +![rasterx example](/doc/TestShapes4.svg.png?raw=true "Rasterx Example") + +The above image shows the effect of using different join modes for a stroked curving path. The top stroked path uses miter (green) or arc (red, yellow, orange) join functions with high miter limit. The middle and lower path shows the effect of using the miter-clip and arc-clip joins, repectively, with different miter-limit values. The black chevrons at the top show different cap and gap functions. + +## Scanner interface + +Rasterx takes the path description of lines, bezier curves, and drawing parameters, and converts them into a set of straight line segments before rasterizing the lines to an image using some method of antialiasing. Rasterx abstracts this last step through the Scanner interface. There are two different structs that satisfy the Scanner interface; ScannerGV and [ScannerFT](https://github.com/srwiley/scanFT). ScannerGV wraps the rasterizer found in the golang.org/x/image/vector package. ScannerFT contains a modified version of the antialiaser found in the [golang freetype](https://github.com/golang/freetype) translation. These use different functions to connect an image to the antialiaser. ScannerFT uses a Painter to translate the raster onto the image, and ScannerGV uses the vector's Draw method with a source image and uses the path as an alpha mask. Please see the test files for examples. At this time, the ScannerFT is a bit faster as compared to ScannerGV for larger and less complicated images, while ScannerGV can be faster for smaller and more complex images. Also ScannerGV does not allow for using the even-odd winding rule, which is something the SVG specification uses. Since ScannerFT is subject to freetype style licensing rules, it lives [here](https://github.com/srwiley/scanFT) in a separate repository and must be imported into your project seperately. ScannerGV is included in the rasterx package, and has more go-friendly licensing. + +Below are the results of some benchmarks performed on a sample shape (the letter Q ). The first test is the time it takes to scan the image after all the curves have been flattened. The second test is the time it takes to flatten, and scan a simple filled image. The last test is the time it takes to flatten a stroked and dashed outline of the shape and scan it. Results for three different image sizes are shown. + + +``` +128x128 Image +Test Rep Time +BenchmarkScanGV-16 5000 287180 ns/op +BenchmarkFillGV-16 5000 339831 ns/op +BenchmarkDashGV-16 2000 968265 ns/op + +BenchmarkScanFT-16 20000 88118 ns/op +BenchmarkFillFT-16 5000 214370 ns/op +BenchmarkDashFT-16 1000 2063797 ns/op + +256x256 Image +Test Rep Time +BenchmarkScanGV-16 2000 1188452 ns/op +BenchmarkFillGV-16 1000 1277268 ns/op +BenchmarkDashGV-16 500 2238169 ns/op + +BenchmarkScanFT-16 5000 290685 ns/op +BenchmarkFillFT-16 3000 446329 ns/op +BenchmarkDashFT-16 500 2923512 ns/op + +512x512 Image +Test Rep Time +BenchmarkScanGV-16 500 3341038 ns/op +BenchmarkFillGV-16 500 4032213 ns/op +BenchmarkDashGV-16 200 6003355 ns/op + +BenchmarkScanFT-16 5000 292884 ns/op +BenchmarkFillFT-16 3000 449582 ns/op +BenchmarkDashFT-16 500 2800493 ns/op +``` + +The package uses an interface called Rasterx, which is satisfied by three structs, Filler, Stroker and Dasher. The Filler flattens Bezier curves into lines and uses an anonymously composed Scanner for the antialiasing step. The Stroker embeds a Filler and adds path stroking, and the Dasher embedds a Stroker and adds the ability to create dashed stroked curves. + + +![rasterx Scheme](/doc/schematic.png?raw=true "Rasterx Scheme") + +Each of the Filler, Dasher, and Stroker can function on their own and each implement the Rasterx interface, so if you need just the curve filling but no stroking capability, you only need a Filler. On the other hand if you have created a Dasher and want to use it to Fill, you can just do this: + +```golang +filler := &dasher.Filler +``` +Now filler is a filling rasterizer. Please see rasterx_test.go for examples. + + +### Non-standard library dependencies +rasterx requires the following imports which are not included in the go standard library: + +* golang.org/x/image/math/fixed +* golang.org/x/image/vector + +These can be included in your gopath by the following 'get' commands: + +* "go get golang.org/x/image/vector" +* "go get golang.org/x/image/math/fixed" + +If you want to use the freetype style antialiaser, 'go get' or clone into your workspace the scanFT package: + +* github.com/srwiley/scanFT + diff --git a/vendor/github.com/srwiley/rasterx/dash.go b/vendor/github.com/srwiley/rasterx/dash.go new file mode 100644 index 0000000000000..fe207710b4ea3 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/dash.go @@ -0,0 +1,202 @@ +// Copyright 2017 by the rasterx Authors. All rights reserved. +//_ +// created: 2017 by S.R.Wiley + +package rasterx + +import ( + "golang.org/x/image/math/fixed" +) + +// Dasher struct extends the Stroker and can draw +// dashed lines with end capping +type Dasher struct { + Stroker + Dashes []fixed.Int26_6 + dashPlace int + firstDashIsGap, dashIsGap bool + deltaDash, DashOffset fixed.Int26_6 + sgm Rasterx + // sgm allows us to switch between dashing + // and non-dashing rasterizers in the SetStroke function. +} + +// joinF overides stroker joinF during dashed stroking, because we need to slightly modify +// the the call as below to handle the case of the join being in a dash gap. +func (r *Dasher) joinF() { + if len(r.Dashes) == 0 || !r.inStroke || !r.dashIsGap { + r.Stroker.joinF() + } +} + +// Start starts a dashed line +func (r *Dasher) Start(a fixed.Point26_6) { + // Advance dashPlace to the dashOffset start point and set deltaDash + if len(r.Dashes) > 0 { + r.deltaDash = r.DashOffset + r.dashIsGap = false + r.dashPlace = 0 + for r.deltaDash > r.Dashes[r.dashPlace] { + r.deltaDash -= r.Dashes[r.dashPlace] + r.dashIsGap = !r.dashIsGap + r.dashPlace++ + if r.dashPlace == len(r.Dashes) { + r.dashPlace = 0 + } + } + r.firstDashIsGap = r.dashIsGap + } + r.Stroker.Start(a) +} + +// lineF overides stroker lineF to modify the the call as below +// while performing the join in a dashed stroke. +func (r *Dasher) lineF(b fixed.Point26_6) { + var bnorm fixed.Point26_6 + a := r.a // Copy local a since r.a is going to change during stroke operation + ba := b.Sub(a) + segLen := Length(ba) + var nlt fixed.Int26_6 + if b == r.leadPoint.P { // End of segment + bnorm = r.leadPoint.TNorm // Use more accurate leadPoint tangent + } else { + bnorm = turnPort90(ToLength(b.Sub(a), r.u)) // Intra segment normal + } + for segLen+r.deltaDash > r.Dashes[r.dashPlace] { + nl := r.Dashes[r.dashPlace] - r.deltaDash + nlt += nl + r.dashLineStrokeBit(a.Add(ToLength(ba, nlt)), bnorm, false) + r.dashIsGap = !r.dashIsGap + segLen -= nl + r.deltaDash = 0 + r.dashPlace++ + if r.dashPlace == len(r.Dashes) { + r.dashPlace = 0 + } + } + r.deltaDash += segLen + r.dashLineStrokeBit(b, bnorm, true) +} + +// SetStroke set the parameters for stroking a line. width is the width of the line, miterlimit is the miter cutoff +// value for miter, arc, miterclip and arcClip joinModes. CapL and CapT are the capping functions for leading and trailing +// line ends. If one is nil, the other function is used at both ends. gp is the gap function that determines how a +// gap on the convex side of two lines joining is filled. jm is the JoinMode for curve segments. Dashes is the values for +// the dash pattern. Pass in nil or an empty slice for no dashes. dashoffset is the starting offset into the dash array. +func (r *Dasher) SetStroke(width, miterLimit fixed.Int26_6, capL, capT CapFunc, gp GapFunc, jm JoinMode, dashes []float64, dashOffset float64) { + r.Stroker.SetStroke(width, miterLimit, capL, capT, gp, jm) + + r.Dashes = r.Dashes[:0] // clear the dash array + if len(dashes) == 0 { + r.sgm = &r.Stroker // This is just plain stroking + return + } + // Dashed Stroke + // Convert the float dash array and offset to fixed point and attach to the Filler + oneIsPos := false // Check to see if at least one dash is > 0 + for _, v := range dashes { + fv := fixed.Int26_6(v * 64) + if fv <= 0 { // Negatives are considered 0s. + fv = 0 + } else { + oneIsPos = true + } + r.Dashes = append(r.Dashes, fv) + } + if oneIsPos == false { + r.Dashes = r.Dashes[:0] + r.sgm = &r.Stroker // This is just plain stroking + return + } + r.DashOffset = fixed.Int26_6(dashOffset * 64) + r.sgm = r // Use the full dasher +} + +//Stop terminates a dashed line +func (r *Dasher) Stop(isClosed bool) { + if len(r.Dashes) == 0 { + r.Stroker.Stop(isClosed) + return + } + if r.inStroke == false { + return + } + if isClosed && r.a != r.firstP.P { + r.LineSeg(r.sgm, r.firstP.P) + } + ra := &r.Filler + if isClosed && !r.firstDashIsGap && !r.dashIsGap { // closed connect w/o caps + a := r.a + r.firstP.TNorm = r.leadPoint.TNorm + r.firstP.RT = r.leadPoint.RT + r.firstP.TTan = r.leadPoint.TTan + ra.Start(r.firstP.P.Sub(r.firstP.TNorm)) + ra.Line(a.Sub(r.ln)) + ra.Start(a.Add(r.ln)) + ra.Line(r.firstP.P.Add(r.firstP.TNorm)) + r.Joiner(r.firstP) + r.firstP.blackWidowMark(ra) + } else { // Cap open ends + if !r.dashIsGap { + r.CapL(ra, r.leadPoint.P, r.leadPoint.TNorm) + } + if !r.firstDashIsGap { + r.CapT(ra, r.firstP.P, Invert(r.firstP.LNorm)) + } + } + r.inStroke = false +} + +// dashLineStrokeBit is a helper function that reduces code redundancey in the +// lineF function. +func (r *Dasher) dashLineStrokeBit(b, bnorm fixed.Point26_6, dontClose bool) { + if !r.dashIsGap { // Moving from dash to gap + a := r.a + ra := &r.Filler + ra.Start(b.Sub(bnorm)) + ra.Line(a.Sub(r.ln)) + ra.Start(a.Add(r.ln)) + ra.Line(b.Add(bnorm)) + if dontClose == false { + r.CapL(ra, b, bnorm) + } + } else { // Moving from gap to dash + if dontClose == false { + ra := &r.Filler + r.CapT(ra, b, Invert(bnorm)) + } + } + r.a = b + r.ln = bnorm +} + +// Line for Dasher is here to pass the dasher sgm to LineP +func (r *Dasher) Line(b fixed.Point26_6) { + r.LineSeg(r.sgm, b) +} + +// QuadBezier for dashing +func (r *Dasher) QuadBezier(b, c fixed.Point26_6) { + r.quadBezierf(r.sgm, b, c) +} + +// CubeBezier starts a stroked cubic bezier. +// It is a low level function exposed for the purposes of callbacks +// and debugging. +func (r *Dasher) CubeBezier(b, c, d fixed.Point26_6) { + r.cubeBezierf(r.sgm, b, c, d) +} + +// NewDasher returns a Dasher ptr with default values. +// A Dasher has all of the capabilities of a Stroker, Filler, and Scanner, plus the ability +// to stroke curves with solid lines. Use SetStroke to configure with non-default +// values. +func NewDasher(width, height int, scanner Scanner) *Dasher { + r := new(Dasher) + r.Scanner = scanner + r.SetBounds(width, height) + r.SetWinding(true) + r.SetStroke(1*64, 4*64, ButtCap, nil, FlatGap, MiterClip, nil, 0) + r.sgm = &r.Stroker + return r +} diff --git a/vendor/github.com/srwiley/rasterx/fill.go b/vendor/github.com/srwiley/rasterx/fill.go new file mode 100644 index 0000000000000..1283bf9d0c79f --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/fill.go @@ -0,0 +1,241 @@ +// Copyright 2018 by the rasterx Authors. All rights reserved. +//_ +// Created 2017 by S.R.Wiley + +package rasterx + +import ( + "image" + "image/color" + "math" + + "golang.org/x/image/math/fixed" +) + +type ( + // ColorFunc maps a color to x y coordinates + ColorFunc func(x, y int) color.Color + // Scanner interface for path generating types + Scanner interface { + Start(a fixed.Point26_6) + Line(b fixed.Point26_6) + Draw() + GetPathExtent() fixed.Rectangle26_6 + SetBounds(w, h int) + SetColor(color interface{}) + SetWinding(useNonZeroWinding bool) + Clear() + + // SetClip sets an optional clipping rectangle to restrict rendering + // only to that region -- if size is 0 then ignored (set to image.ZR + // to clear) + SetClip(rect image.Rectangle) + } + // Adder interface for types that can accumlate path commands + Adder interface { + // Start starts a new curve at the given point. + Start(a fixed.Point26_6) + // Line adds a line segment to the path + Line(b fixed.Point26_6) + // QuadBezier adds a quadratic bezier curve to the path + QuadBezier(b, c fixed.Point26_6) + // CubeBezier adds a cubic bezier curve to the path + CubeBezier(b, c, d fixed.Point26_6) + // Closes the path to the start point if closeLoop is true + Stop(closeLoop bool) + } + // Rasterx extends the adder interface to include lineF and joinF functions + Rasterx interface { + Adder + lineF(b fixed.Point26_6) + joinF() + } + + // Filler satisfies Rasterx + Filler struct { + Scanner + a, first fixed.Point26_6 + } +) + +// Start starts a new path at the given point. +func (r *Filler) Start(a fixed.Point26_6) { + r.a = a + r.first = a + r.Scanner.Start(a) +} + +// Stop sends a path at the given point. +func (r *Filler) Stop(isClosed bool) { + if r.first != r.a { + r.Line(r.first) + } +} + +// QuadBezier adds a quadratic segment to the current curve. +func (r *Filler) QuadBezier(b, c fixed.Point26_6) { + r.QuadBezierF(r, b, c) +} + +// QuadTo flattens the quadratic Bezier curve into lines through the LineTo func +// This functions is adapted from the version found in +// golang.org/x/image/vector +func QuadTo(ax, ay, bx, by, cx, cy float32, LineTo func(dx, dy float32)) { + devsq := devSquared(ax, ay, bx, by, cx, cy) + if devsq >= 0.333 { + const tol = 3 + n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq)))) + t, nInv := float32(0), 1/float32(n) + for i := 0; i < n-1; i++ { + t += nInv + + mt := 1 - t + t1 := mt * mt + t2 := mt * t * 2 + t3 := t * t + LineTo( + ax*t1+bx*t2+cx*t3, + ay*t1+by*t2+cy*t3) + } + } + LineTo(cx, cy) +} + +// CubeTo flattens the cubic Bezier curve into lines through the LineTo func +// This functions is adapted from the version found in +// golang.org/x/image/vector +func CubeTo(ax, ay, bx, by, cx, cy, dx, dy float32, LineTo func(ex, ey float32)) { + devsq := devSquared(ax, ay, bx, by, dx, dy) + if devsqAlt := devSquared(ax, ay, cx, cy, dx, dy); devsq < devsqAlt { + devsq = devsqAlt + } + if devsq >= 0.333 { + const tol = 3 + n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq)))) + t, nInv := float32(0), 1/float32(n) + for i := 0; i < n-1; i++ { + t += nInv + + tsq := t * t + mt := 1 - t + mtsq := mt * mt + t1 := mtsq * mt + t2 := mtsq * t * 3 + t3 := mt * tsq * 3 + t4 := tsq * t + LineTo( + ax*t1+bx*t2+cx*t3+dx*t4, + ay*t1+by*t2+cy*t3+dy*t4) + } + } + LineTo(dx, dy) +} + +// devSquared returns a measure of how curvy the sequence (ax, ay) to (bx, by) +// to (cx, cy) is. It determines how many line segments will approximate a +// Bézier curve segment. This functions is copied from the version found in +// golang.org/x/image/vector as are the below comments. +// +// http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html +// gives the rationale for this evenly spaced heuristic instead of a recursive +// de Casteljau approach: +// +// The reason for the subdivision by n is that I expect the "flatness" +// computation to be semi-expensive (it's done once rather than on each +// potential subdivision) and also because you'll often get fewer subdivisions. +// Taking a circular arc as a simplifying assumption (ie a spherical cow), +// where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't +// made any horrible mistakes, is expected to be 33% more in the limit. +func devSquared(ax, ay, bx, by, cx, cy float32) float32 { + devx := ax - 2*bx + cx + devy := ay - 2*by + cy + return devx*devx + devy*devy +} + +// QuadBezierF adds a quadratic segment to the sgm Rasterizer. +func (r *Filler) QuadBezierF(sgm Rasterx, b, c fixed.Point26_6) { + // check for degenerate bezier + if r.a == b || b == c { + sgm.Line(c) + return + } + sgm.joinF() + QuadTo(float32(r.a.X), float32(r.a.Y), // Pts are x64, but does not matter. + float32(b.X), float32(b.Y), + float32(c.X), float32(c.Y), + func(dx, dy float32) { + sgm.lineF(fixed.Point26_6{X: fixed.Int26_6(dx), Y: fixed.Int26_6(dy)}) + }) + +} + +// CubeBezier adds a cubic bezier to the curve +func (r *Filler) CubeBezier(b, c, d fixed.Point26_6) { + r.CubeBezierF(r, b, c, d) +} + +// joinF is a no-op for a filling rasterizer. This is used in stroking and dashed +// stroking +func (r *Filler) joinF() { + +} + +// Line for a filling rasterizer is just the line call in scan +func (r *Filler) Line(b fixed.Point26_6) { + r.lineF(b) +} + +// lineF for a filling rasterizer is just the line call in scan +func (r *Filler) lineF(b fixed.Point26_6) { + r.Scanner.Line(b) + r.a = b +} + +// CubeBezierF adds a cubic bezier to the curve. sending the line calls the the +// sgm Rasterizer +func (r *Filler) CubeBezierF(sgm Rasterx, b, c, d fixed.Point26_6) { + if (r.a == b && c == d) || (r.a == b && b == c) || (c == b && d == c) { + sgm.Line(d) + return + } + sgm.joinF() + CubeTo(float32(r.a.X), float32(r.a.Y), + float32(b.X), float32(b.Y), + float32(c.X), float32(c.Y), + float32(d.X), float32(d.Y), + func(ex, ey float32) { + sgm.lineF(fixed.Point26_6{X: fixed.Int26_6(ex), Y: fixed.Int26_6(ey)}) + }) +} + +// Clear resets the filler +func (r *Filler) Clear() { + r.a = fixed.Point26_6{} + r.first = r.a + r.Scanner.Clear() +} + +// SetBounds sets the maximum width and height of the rasterized image and +// calls Clear. The width and height are in pixels, not fixed.Int26_6 units. +func (r *Filler) SetBounds(width, height int) { + if width < 0 { + width = 0 + } + if height < 0 { + height = 0 + } + r.Scanner.SetBounds(width, height) + r.Clear() +} + +// NewFiller returns a Filler ptr with default values. +// A Filler in addition to rasterizing lines like a Scann, +// can also rasterize quadratic and cubic bezier curves. +// If Scanner is nil default scanner ScannerGV is used +func NewFiller(width, height int, scanner Scanner) *Filler { + r := new(Filler) + r.Scanner = scanner + r.SetBounds(width, height) + r.SetWinding(true) + return r +} diff --git a/vendor/github.com/srwiley/rasterx/geomx.go b/vendor/github.com/srwiley/rasterx/geomx.go new file mode 100644 index 0000000000000..48976d302f46e --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/geomx.go @@ -0,0 +1,301 @@ +// geomx adds some geometry functions needed by rasterx +// Copyright 2017 by the rasterx Authors. All rights reserved. +// Created: 2/12/2017 by S.R.Wiley + +package rasterx + +import ( + "fmt" + "math" + + "golang.org/x/image/math/fixed" +) + +// Invert returns the point inverted around the origin +func Invert(v fixed.Point26_6) fixed.Point26_6 { + return fixed.Point26_6{X: -v.X, Y: -v.Y} +} + +// turnStarboard90 returns the vector 90 degrees starboard (right in direction heading) +func turnStarboard90(v fixed.Point26_6) fixed.Point26_6 { + return fixed.Point26_6{X: -v.Y, Y: v.X} +} + +// turnPort90 returns the vector 90 degrees port (left in direction heading) +func turnPort90(v fixed.Point26_6) fixed.Point26_6 { + return fixed.Point26_6{X: v.Y, Y: -v.X} +} + +// DotProd returns the inner product of p and q +func DotProd(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 { + return fixed.Int52_12(int64(p.X)*int64(q.X) + int64(p.Y)*int64(q.Y)) +} + +// Length is the distance from the origin of the point +func Length(v fixed.Point26_6) fixed.Int26_6 { + vx, vy := float64(v.X), float64(v.Y) + return fixed.Int26_6(math.Sqrt(vx*vx + vy*vy)) +} + +//PathCommand is the type for the path command token +type PathCommand fixed.Int26_6 + +// Human readable path constants +const ( + PathMoveTo PathCommand = iota + PathLineTo + PathQuadTo + PathCubicTo + PathClose +) + +// A Path starts with a PathCommand value followed by zero to three fixed +// int points. +type Path []fixed.Int26_6 + +// ToSVGPath returns a string representation of the path +func (p Path) ToSVGPath() string { + s := "" + for i := 0; i < len(p); { + if i != 0 { + s += " " + } + switch PathCommand(p[i]) { + case PathMoveTo: + s += fmt.Sprintf("M%4.3f,%4.3f", float32(p[i+1])/64, float32(p[i+2])/64) + i += 3 + case PathLineTo: + s += fmt.Sprintf("L%4.3f,%4.3f", float32(p[i+1])/64, float32(p[i+2])/64) + i += 3 + case PathQuadTo: + s += fmt.Sprintf("Q%4.3f,%4.3f,%4.3f,%4.3f", float32(p[i+1])/64, float32(p[i+2])/64, + float32(p[i+3])/64, float32(p[i+4])/64) + i += 5 + case PathCubicTo: + s += "C" + fmt.Sprintf("C%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f", float32(p[i+1])/64, float32(p[i+2])/64, + float32(p[i+3])/64, float32(p[i+4])/64, float32(p[i+5])/64, float32(p[i+6])/64) + i += 7 + case PathClose: + s += "Z" + i++ + default: + panic("freetype/rasterx: bad pather") + } + } + return s +} + +// String returns a readable representation of a Path. +func (p Path) String() string { + return p.ToSVGPath() +} + +// Clear zeros the path slice +func (p *Path) Clear() { + *p = (*p)[:0] +} + +// Start starts a new curve at the given point. +func (p *Path) Start(a fixed.Point26_6) { + *p = append(*p, fixed.Int26_6(PathMoveTo), a.X, a.Y) +} + +// Line adds a linear segment to the current curve. +func (p *Path) Line(b fixed.Point26_6) { + *p = append(*p, fixed.Int26_6(PathLineTo), b.X, b.Y) +} + +// QuadBezier adds a quadratic segment to the current curve. +func (p *Path) QuadBezier(b, c fixed.Point26_6) { + *p = append(*p, fixed.Int26_6(PathQuadTo), b.X, b.Y, c.X, c.Y) +} + +// CubeBezier adds a cubic segment to the current curve. +func (p *Path) CubeBezier(b, c, d fixed.Point26_6) { + *p = append(*p, fixed.Int26_6(PathCubicTo), b.X, b.Y, c.X, c.Y, d.X, d.Y) +} + +// Stop joins the ends of the path +func (p *Path) Stop(closeLoop bool) { + if closeLoop { + *p = append(*p, fixed.Int26_6(PathClose)) + } +} + +// AddTo adds the Path p to q. +func (p Path) AddTo(q Adder) { + for i := 0; i < len(p); { + switch PathCommand(p[i]) { + case PathMoveTo: + q.Stop(false) // Fixes issues #1 by described by Djadala; implicit close if currently in path. + q.Start(fixed.Point26_6{X: p[i+1], Y: p[i+2]}) + i += 3 + case PathLineTo: + q.Line(fixed.Point26_6{X: p[i+1], Y: p[i+2]}) + i += 3 + case PathQuadTo: + q.QuadBezier(fixed.Point26_6{X: p[i+1], Y: p[i+2]}, fixed.Point26_6{X: p[i+3], Y: p[i+4]}) + i += 5 + case PathCubicTo: + q.CubeBezier(fixed.Point26_6{X: p[i+1], Y: p[i+2]}, + fixed.Point26_6{X: p[i+3], Y: p[i+4]}, fixed.Point26_6{X: p[i+5], Y: p[i+6]}) + i += 7 + case PathClose: + q.Stop(true) + i++ + default: + panic("AddTo: bad path") + } + } + q.Stop(false) +} + +// ToLength scales the point to the length indicated by ln +func ToLength(p fixed.Point26_6, ln fixed.Int26_6) (q fixed.Point26_6) { + if ln == 0 || (p.X == 0 && p.Y == 0) { + return + } + lp := Length(p) + q.X, q.Y = p.X*ln/lp, p.Y*ln/lp + return +} + +// ClosestPortside returns the closest of p1 or p2 on the port side of the +// line from the bow to the stern. (port means left side of the direction you are heading) +// isIntersecting is just convienice to reduce code, and if false returns false, because p1 and p2 are not valid +func ClosestPortside(bow, stern, p1, p2 fixed.Point26_6, isIntersecting bool) (xt fixed.Point26_6, intersects bool) { + if isIntersecting == false { + return + } + dir := bow.Sub(stern) + dp1 := p1.Sub(stern) + dp2 := p2.Sub(stern) + cp1 := dir.X*dp1.Y - dp1.X*dir.Y + cp2 := dir.X*dp2.Y - dp2.X*dir.Y + switch { + case cp1 < 0 && cp2 < 0: + return + case cp1 < 0 && cp2 >= 0: + return p2, true + case cp1 >= 0 && cp2 < 0: + return p1, true + default: // both points on port side + dirdot := DotProd(dir, dir) + // calculate vector rejections of dp1 and dp2 onto dir + h1 := dp1.Sub(dir.Mul(fixed.Int26_6((DotProd(dp1, dir) << 6) / dirdot))) + h2 := dp2.Sub(dir.Mul(fixed.Int26_6((DotProd(dp2, dir) << 6) / dirdot))) + // return point with smallest vector rejection; i.e. closest to dir line + if (h1.X*h1.X + h1.Y*h1.Y) > (h2.X*h2.X + h2.Y*h2.Y) { + return p2, true + } + return p1, true + } +} + +// RadCurvature returns the curvature of a Bezier curve end point, +// given an end point, the two adjacent control points and the degree. +// The sign of the value indicates if the center of the osculating circle +// is left or right (port or starboard) of the curve in the forward direction. +func RadCurvature(p0, p1, p2 fixed.Point26_6, dm fixed.Int52_12) fixed.Int26_6 { + a, b := p2.Sub(p1), p1.Sub(p0) + abdot, bbdot := DotProd(a, b), DotProd(b, b) + h := a.Sub(b.Mul(fixed.Int26_6((abdot << 6) / bbdot))) // h is the vector rejection of a onto b + if h.X == 0 && h.Y == 0 { // points are co-linear + return 0 + } + radCurve := fixed.Int26_6((fixed.Int52_12(a.X*a.X+a.Y*a.Y) * dm / fixed.Int52_12(Length(h)<<6)) >> 6) + if a.X*b.Y > b.X*a.Y { // xprod sign + return radCurve + } + return -radCurve +} + +// CircleCircleIntersection calculates the points of intersection of +// two circles or returns with intersects == false if no such points exist. +func CircleCircleIntersection(ct, cl fixed.Point26_6, rt, rl fixed.Int26_6) (xt1, xt2 fixed.Point26_6, intersects bool) { + dc := cl.Sub(ct) + d := Length(dc) + + // Check for solvability. + if d > (rt + rl) { + return // No solution. Circles do not intersect. + } + // check if d < abs(rt-rl) + if da := rt - rl; (da > 0 && d < da) || (da < 0 && d < -da) { + return // No solution. One circle is contained by the other. + } + + rlf, rtf, df := float64(rl), float64(rt), float64(d) + af := (rtf*rtf - rlf*rlf + df*df) / df / 2.0 + hfd := math.Sqrt(rtf*rtf-af*af) / df + afd := af / df + + rOffx, rOffy := float64(-dc.Y)*hfd, float64(dc.X)*hfd + p2x := float64(ct.X) + float64(dc.X)*afd + p2y := float64(ct.Y) + float64(dc.Y)*afd + xt1x, xt1y := p2x+rOffx, p2y+rOffy + xt2x, xt2y := p2x-rOffx, p2y-rOffy + return fixed.Point26_6{X: fixed.Int26_6(xt1x), Y: fixed.Int26_6(xt1y)}, + fixed.Point26_6{X: fixed.Int26_6(xt2x), Y: fixed.Int26_6(xt2y)}, true +} + +// CalcIntersect calculates the points of intersection of two fixed point lines +// and panics if the determinate is zero. You have been warned. +func CalcIntersect(a1, a2, b1, b2 fixed.Point26_6) (x fixed.Point26_6) { + da, db, ds := a2.Sub(a1), b2.Sub(b1), a1.Sub(b1) + det := float32(da.X*db.Y - db.X*da.Y) // Determinate + t := float32(ds.Y*db.X-ds.X*db.Y) / det + x = a1.Add(fixed.Point26_6{X: fixed.Int26_6(float32(da.X) * t), Y: fixed.Int26_6(float32(da.Y) * t)}) + return +} + +// RayCircleIntersection calculates the points of intersection of +// a ray starting at s2 passing through s1 and a circle in fixed point. +// Returns intersects == false if no solution is possible. If two +// solutions are possible, the point closest to s2 is returned +func RayCircleIntersection(s1, s2, c fixed.Point26_6, r fixed.Int26_6) (x fixed.Point26_6, intersects bool) { + fx, fy, intersects := RayCircleIntersectionF(float64(s1.X), float64(s1.Y), + float64(s2.X), float64(s2.Y), float64(c.X), float64(c.Y), float64(r)) + return fixed.Point26_6{X: fixed.Int26_6(fx), + Y: fixed.Int26_6(fy)}, intersects + +} + +// RayCircleIntersectionF calculates in floating point the points of intersection of +// a ray starting at s2 passing through s1 and a circle in fixed point. +// Returns intersects == false if no solution is possible. If two +// solutions are possible, the point closest to s2 is returned +func RayCircleIntersectionF(s1X, s1Y, s2X, s2Y, cX, cY, r float64) (x, y float64, intersects bool) { + n := s2X - cX // Calculating using 64* rather than divide + m := s2Y - cY + + e := s2X - s1X + d := s2Y - s1Y + + // Quadratic normal form coefficients + A, B, C := e*e+d*d, -2*(e*n+m*d), n*n+m*m-r*r + + D := B*B - 4*A*C + + if D <= 0 { + return // No intersection or is tangent + } + + D = math.Sqrt(D) + t1, t2 := (-B+D)/(2*A), (-B-D)/(2*A) + p1OnSide := t1 > 0 + p2OnSide := t2 > 0 + + switch { + case p1OnSide && p2OnSide: + if t2 < t1 { // both on ray, use closest to s2 + t1 = t2 + } + case p2OnSide: // Only p2 on ray + t1 = t2 + case p1OnSide: // only p1 on ray + default: // Neither solution is on the ray + return + } + return (n - e*t1) + cX, (m - d*t1) + cY, true +} diff --git a/vendor/github.com/srwiley/rasterx/gradient.go b/vendor/github.com/srwiley/rasterx/gradient.go new file mode 100644 index 0000000000000..f7e3841e00ce9 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/gradient.go @@ -0,0 +1,310 @@ +// Gradient implementation fo rasterx package +// Copyright 2018 All rights reserved. +// Created: 5/12/2018 by S.R.Wiley + +package rasterx + +import ( + "image/color" + "math" + "sort" +) + +// SVG bounds paremater constants +const ( + ObjectBoundingBox GradientUnits = iota + UserSpaceOnUse +) + +// SVG spread parameter constants +const ( + PadSpread SpreadMethod = iota + ReflectSpread + RepeatSpread +) + +const epsilonF = 1e-5 + +type ( + // SpreadMethod is the type for spread parameters + SpreadMethod byte + // GradientUnits is the type for gradient units + GradientUnits byte + // GradStop represents a stop in the SVG 2.0 gradient specification + GradStop struct { + StopColor color.Color + Offset float64 + Opacity float64 + } + // Gradient holds a description of an SVG 2.0 gradient + Gradient struct { + Points [5]float64 + Stops []GradStop + Bounds struct{ X, Y, W, H float64 } + Matrix Matrix2D + Spread SpreadMethod + Units GradientUnits + IsRadial bool + } +) + +// ApplyOpacity sets the color's alpha channel to the given value +func ApplyOpacity(c color.Color, opacity float64) color.NRGBA { + r, g, b, _ := c.RGBA() + return color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(opacity * 0xFF)} +} + +// tColor takes the paramaterized value along the gradient's stops and +// returns a color depending on the spreadMethod value of the gradient and +// the gradient's slice of stop values. +func (g *Gradient) tColor(t, opacity float64) color.Color { + d := len(g.Stops) + // These cases can be taken care of early on + if t >= 1.0 && g.Spread == PadSpread { + s := g.Stops[d-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + } + if t <= 0.0 && g.Spread == PadSpread { + return ApplyOpacity(g.Stops[0].StopColor, g.Stops[0].Opacity*opacity) + } + + var modRange = 1.0 + if g.Spread == ReflectSpread { + modRange = 2.0 + } + mod := math.Mod(t, modRange) + if mod < 0 { + mod += modRange + } + + place := 0 // Advance to place where mod is greater than the indicated stop + for place != len(g.Stops) && mod > g.Stops[place].Offset { + place++ + } + switch g.Spread { + case RepeatSpread: + var s1, s2 GradStop + switch place { + case 0, d: + s1, s2 = g.Stops[d-1], g.Stops[0] + default: + s1, s2 = g.Stops[place-1], g.Stops[place] + } + return g.blendStops(mod, opacity, s1, s2, false) + case ReflectSpread: + switch place { + case 0: + return ApplyOpacity(g.Stops[0].StopColor, g.Stops[0].Opacity*opacity) + case d: + // Advance to place where mod-1 is greater than the stop indicated by place in reverse of the stop slice. + // Since this is the reflect spead mode, the mod interval is two, allowing the stop list to be + // iterated in reverse before repeating the sequence. + for place != d*2 && mod-1 > (1-g.Stops[d*2-place-1].Offset) { + place++ + } + switch place { + case d: + s := g.Stops[d-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + case d * 2: + return ApplyOpacity(g.Stops[0].StopColor, g.Stops[0].Opacity*opacity) + default: + return g.blendStops(mod-1, opacity, + g.Stops[d*2-place], g.Stops[d*2-place-1], true) + } + default: + return g.blendStops(mod, opacity, + g.Stops[place-1], g.Stops[place], false) + } + default: // PadSpread + switch place { + case 0: + return ApplyOpacity(g.Stops[0].StopColor, g.Stops[0].Opacity*opacity) + case len(g.Stops): + s := g.Stops[len(g.Stops)-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + default: + return g.blendStops(mod, opacity, g.Stops[place-1], g.Stops[place], false) + } + } +} + +func (g *Gradient) blendStops(t, opacity float64, s1, s2 GradStop, flip bool) color.Color { + s1off := s1.Offset + if s1.Offset > s2.Offset && !flip { // happens in repeat spread mode + s1off-- + if t > 1 { + t-- + } + } + if s2.Offset == s1off { + return ApplyOpacity(s2.StopColor, s2.Opacity) + } + if flip { + t = 1 - t + } + tp := (t - s1off) / (s2.Offset - s1off) + r1, g1, b1, _ := s1.StopColor.RGBA() + r2, g2, b2, _ := s2.StopColor.RGBA() + + return ApplyOpacity(color.RGBA{ + uint8((float64(r1)*(1-tp) + float64(r2)*tp) / 256), + uint8((float64(g1)*(1-tp) + float64(g2)*tp) / 256), + uint8((float64(b1)*(1-tp) + float64(b2)*tp) / 256), + 0xFF}, (s1.Opacity*(1-tp)+s2.Opacity*tp)*opacity) +} + +//GetColorFunction returns the color function +func (g *Gradient) GetColorFunction(opacity float64) interface{} { + return g.GetColorFunctionUS(opacity, Identity) +} + +//GetColorFunctionUS returns the color function using the User Space objMatrix +func (g *Gradient) GetColorFunctionUS(opacity float64, objMatrix Matrix2D) interface{} { + switch len(g.Stops) { + case 0: + return ApplyOpacity(color.RGBA{0, 0, 0, 255}, opacity) // default error color for gradient w/o stops. + case 1: + return ApplyOpacity(g.Stops[0].StopColor, opacity) // Illegal, I think, should really should not happen. + } + + // sort by offset in ascending order + sort.Slice(g.Stops, func(i, j int) bool { + return g.Stops[i].Offset < g.Stops[j].Offset + }) + + w, h := float64(g.Bounds.W), float64(g.Bounds.H) + oriX, oriY := float64(g.Bounds.X), float64(g.Bounds.Y) + gradT := Identity.Translate(oriX, oriY).Scale(w, h). + Mult(g.Matrix).Scale(1/w, 1/h).Translate(-oriX, -oriY).Invert() + + if g.IsRadial { + cx, cy, fx, fy, rx, ry := g.Points[0], g.Points[1], g.Points[2], g.Points[3], g.Points[4], g.Points[4] + if g.Units == ObjectBoundingBox { + cx = g.Bounds.X + g.Bounds.W*cx + cy = g.Bounds.Y + g.Bounds.H*cy + fx = g.Bounds.X + g.Bounds.W*fx + fy = g.Bounds.Y + g.Bounds.H*fy + rx *= g.Bounds.W + ry *= g.Bounds.H + } else { + cx, cy = g.Matrix.Transform(cx, cy) + fx, fy = g.Matrix.Transform(fx, fy) + rx, ry = g.Matrix.TransformVector(rx, ry) + cx, cy = objMatrix.Transform(cx, cy) + fx, fy = objMatrix.Transform(fx, fy) + rx, ry = objMatrix.TransformVector(rx, ry) + } + + if cx == fx && cy == fy { + // When the focus and center are the same things are much simpler; + // t is just distance from center + // scaled by the bounds aspect ratio times r + if g.Units == ObjectBoundingBox { + return ColorFunc(func(xi, yi int) color.Color { + x, y := gradT.Transform(float64(xi)+0.5, float64(yi)+0.5) + dx := float64(x) - cx + dy := float64(y) - cy + return g.tColor(math.Sqrt(dx*dx/(rx*rx)+(dy*dy)/(ry*ry)), opacity) + }) + } + return ColorFunc(func(xi, yi int) color.Color { + x := float64(xi) + 0.5 + y := float64(yi) + 0.5 + dx := x - cx + dy := y - cy + return g.tColor(math.Sqrt(dx*dx/(rx*rx)+(dy*dy)/(ry*ry)), opacity) + }) + } + fx /= rx + fy /= ry + cx /= rx + cy /= ry + + dfx := fx - cx + dfy := fy - cy + + if dfx*dfx+dfy*dfy > 1 { // Focus outside of circle; use intersection + // point of line from center to focus and circle as per SVG specs. + nfx, nfy, intersects := RayCircleIntersectionF(fx, fy, cx, cy, cx, cy, 1.0-epsilonF) + fx, fy = nfx, nfy + if intersects == false { + return color.RGBA{255, 255, 0, 255} // should not happen + } + } + if g.Units == ObjectBoundingBox { + return ColorFunc(func(xi, yi int) color.Color { + x, y := gradT.Transform(float64(xi)+0.5, float64(yi)+0.5) + ex := x / rx + ey := y / ry + + t1x, t1y, intersects := RayCircleIntersectionF(ex, ey, fx, fy, cx, cy, 1.0) + if intersects == false { //In this case, use the last stop color + s := g.Stops[len(g.Stops)-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + } + tdx, tdy := t1x-fx, t1y-fy + dx, dy := ex-fx, ey-fy + if tdx*tdx+tdy*tdy < epsilonF { + s := g.Stops[len(g.Stops)-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + } + return g.tColor(math.Sqrt(dx*dx+dy*dy)/math.Sqrt(tdx*tdx+tdy*tdy), opacity) + }) + } + return ColorFunc(func(xi, yi int) color.Color { + x := float64(xi) + 0.5 + y := float64(yi) + 0.5 + ex := x / rx + ey := y / ry + + t1x, t1y, intersects := RayCircleIntersectionF(ex, ey, fx, fy, cx, cy, 1.0) + if intersects == false { //In this case, use the last stop color + s := g.Stops[len(g.Stops)-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + } + tdx, tdy := t1x-fx, t1y-fy + dx, dy := ex-fx, ey-fy + if tdx*tdx+tdy*tdy < epsilonF { + s := g.Stops[len(g.Stops)-1] + return ApplyOpacity(s.StopColor, s.Opacity*opacity) + } + return g.tColor(math.Sqrt(dx*dx+dy*dy)/math.Sqrt(tdx*tdx+tdy*tdy), opacity) + }) + } + p1x, p1y, p2x, p2y := g.Points[0], g.Points[1], g.Points[2], g.Points[3] + if g.Units == ObjectBoundingBox { + p1x = g.Bounds.X + g.Bounds.W*p1x + p1y = g.Bounds.Y + g.Bounds.H*p1y + p2x = g.Bounds.X + g.Bounds.W*p2x + p2y = g.Bounds.Y + g.Bounds.H*p2y + + dx := p2x - p1x + dy := p2y - p1y + d := (dx*dx + dy*dy) // self inner prod + return ColorFunc(func(xi, yi int) color.Color { + x, y := gradT.Transform(float64(xi)+0.5, float64(yi)+0.5) + dfx := x - p1x + dfy := y - p1y + return g.tColor((dx*dfx+dy*dfy)/d, opacity) + }) + } + + p1x, p1y = g.Matrix.Transform(p1x, p1y) + p2x, p2y = g.Matrix.Transform(p2x, p2y) + p1x, p1y = objMatrix.Transform(p1x, p1y) + p2x, p2y = objMatrix.Transform(p2x, p2y) + dx := p2x - p1x + dy := p2y - p1y + d := (dx*dx + dy*dy) + // if d == 0.0 { + // fmt.Println("zero delta") + // } + return ColorFunc(func(xi, yi int) color.Color { + x := float64(xi) + 0.5 + y := float64(yi) + 0.5 + dfx := x - p1x + dfy := y - p1y + return g.tColor((dx*dfx+dy*dfy)/d, opacity) + }) +} diff --git a/vendor/github.com/srwiley/rasterx/matrix.go b/vendor/github.com/srwiley/rasterx/matrix.go new file mode 100644 index 0000000000000..a72bb818039d4 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/matrix.go @@ -0,0 +1,191 @@ +// Implements SVG style matrix transformations. +// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform +// Copyright 2018 All rights reserved. + +package rasterx + +import ( + "math" + + "golang.org/x/image/math/fixed" +) + +// Matrix2D represents an SVG style matrix +type Matrix2D struct { + A, B, C, D, E, F float64 +} + +// matrix3 is a full 3x3 float64 matrix +// used for inverting +type matrix3 [9]float64 + +func otherPair(i int) (a, b int) { + switch i { + case 0: + a, b = 1, 2 + case 1: + a, b = 0, 2 + case 2: + a, b = 0, 1 + } + return +} + +func (m *matrix3) coFact(i, j int) float64 { + ai, bi := otherPair(i) + aj, bj := otherPair(j) + a, b, c, d := m[ai+aj*3], m[bi+bj*3], m[ai+bj*3], m[bi+aj*3] + return a*b - c*d +} + +func (m *matrix3) Invert() *matrix3 { + var cofact matrix3 + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + sign := float64(1 - (i+j%2)%2*2) // "checkerboard of minuses" grid + cofact[i+j*3] = m.coFact(i, j) * sign + } + } + deteriminate := m[0]*cofact[0] + m[1]*cofact[1] + m[2]*cofact[2] + + // transpose cofact + for i := 0; i < 2; i++ { + for j := i + 1; j < 3; j++ { + cofact[i+j*3], cofact[j+i*3] = cofact[j+i*3], cofact[i+j*3] + } + } + for i := 0; i < 9; i++ { + cofact[i] /= deteriminate + } + return &cofact +} + +// Invert returns the inverse matrix +func (a Matrix2D) Invert() Matrix2D { + n := &matrix3{a.A, a.C, a.E, a.B, a.D, a.F, 0, 0, 1} + n = n.Invert() + return Matrix2D{A: n[0], C: n[1], E: n[2], B: n[3], D: n[4], F: n[5]} +} + +// Mult returns a*b +func (a Matrix2D) Mult(b Matrix2D) Matrix2D { + return Matrix2D{ + A: a.A*b.A + a.C*b.B, + B: a.B*b.A + a.D*b.B, + C: a.A*b.C + a.C*b.D, + D: a.B*b.C + a.D*b.D, + E: a.A*b.E + a.C*b.F + a.E, + F: a.B*b.E + a.D*b.F + a.F} +} + +// Identity is the identity matrix +var Identity = Matrix2D{1, 0, 0, 1, 0, 0} + +// TFixed transforms a fixed.Point26_6 by the matrix +func (a Matrix2D) TFixed(x fixed.Point26_6) (y fixed.Point26_6) { + y.X = fixed.Int26_6((float64(x.X)*a.A + float64(x.Y)*a.C) + a.E*64) + y.Y = fixed.Int26_6((float64(x.X)*a.B + float64(x.Y)*a.D) + a.F*64) + return +} + +// Transform multiples the input vector by matrix m and outputs the results vector +// components. +func (a Matrix2D) Transform(x1, y1 float64) (x2, y2 float64) { + x2 = x1*a.A + y1*a.C + a.E + y2 = x1*a.B + y1*a.D + a.F + return +} + +// TransformVector is a modidifed version of Transform that ignores the +// translation components. +func (a Matrix2D) TransformVector(x1, y1 float64) (x2, y2 float64) { + x2 = x1*a.A + y1*a.C + y2 = x1*a.B + y1*a.D + return +} + +//Scale matrix in x and y dimensions +func (a Matrix2D) Scale(x, y float64) Matrix2D { + return a.Mult(Matrix2D{ + A: x, + B: 0, + C: 0, + D: y, + E: 0, + F: 0}) +} + +//SkewY skews the matrix in the Y dimension +func (a Matrix2D) SkewY(theta float64) Matrix2D { + return a.Mult(Matrix2D{ + A: 1, + B: math.Tan(theta), + C: 0, + D: 1, + E: 0, + F: 0}) +} + +//SkewX skews the matrix in the X dimension +func (a Matrix2D) SkewX(theta float64) Matrix2D { + return a.Mult(Matrix2D{ + A: 1, + B: 0, + C: math.Tan(theta), + D: 1, + E: 0, + F: 0}) +} + +//Translate translates the matrix to the x , y point +func (a Matrix2D) Translate(x, y float64) Matrix2D { + return a.Mult(Matrix2D{ + A: 1, + B: 0, + C: 0, + D: 1, + E: x, + F: y}) +} + +//Rotate rotate the matrix by theta +func (a Matrix2D) Rotate(theta float64) Matrix2D { + return a.Mult(Matrix2D{ + A: math.Cos(theta), + B: math.Sin(theta), + C: -math.Sin(theta), + D: math.Cos(theta), + E: 0, + F: 0}) +} + +// MatrixAdder is an adder that applies matrix M to all points +type MatrixAdder struct { + Adder + M Matrix2D +} + +// Reset sets the matrix M to identity +func (t *MatrixAdder) Reset() { + t.M = Identity +} + +// Start starts a new path +func (t *MatrixAdder) Start(a fixed.Point26_6) { + t.Adder.Start(t.M.TFixed(a)) +} + +// Line adds a linear segment to the current curve. +func (t *MatrixAdder) Line(b fixed.Point26_6) { + t.Adder.Line(t.M.TFixed(b)) +} + +// QuadBezier adds a quadratic segment to the current curve. +func (t *MatrixAdder) QuadBezier(b, c fixed.Point26_6) { + t.Adder.QuadBezier(t.M.TFixed(b), t.M.TFixed(c)) +} + +// CubeBezier adds a cubic segment to the current curve. +func (t *MatrixAdder) CubeBezier(b, c, d fixed.Point26_6) { + t.Adder.CubeBezier(t.M.TFixed(b), t.M.TFixed(c), t.M.TFixed(d)) +} diff --git a/vendor/github.com/srwiley/rasterx/scan.go b/vendor/github.com/srwiley/rasterx/scan.go new file mode 100644 index 0000000000000..49a2a08de69b8 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/scan.go @@ -0,0 +1,181 @@ +// Package rasterx implements a rasterizer in go. +// By default rasterx uses ScannerGV to render images +// which uses the rasterizer in the golang.org/x/image/vector package. +// The freetype rasterizer under the GNU license can also be used, by +// downloading the scanFT package. +// +// Copyright 2018 All rights reserved. +// Created: 5/12/2018 by S.R.Wiley +package rasterx + +import ( + "image" + "math" + + "image/color" + "image/draw" + + "golang.org/x/image/math/fixed" + "golang.org/x/image/vector" +) + +// At returns the color at the point x,y +func (c *ColorFuncImage) At(x, y int) color.Color { + return c.colorFunc(x, y) +} + +type ( + // ColorFuncImage implements and image + // using the provided colorFunc + ColorFuncImage struct { + image.Uniform + colorFunc ColorFunc + } + + // ScannerGV uses the google vector rasterizer + ScannerGV struct { + r vector.Rasterizer + //a, first fixed.Point26_6 + Dest draw.Image + Targ image.Rectangle + clipImage *ClipImage + Source image.Image + Offset image.Point + minX, minY, maxX, maxY fixed.Int26_6 // keep track of bounds + } +) + +// ClipImage is a clipable ColorFuncImage +type ClipImage struct { + ColorFuncImage + clip image.Rectangle +} + +var noApha = color.RGBA{0, 0, 0, 0} + +// GetPathExtent returns the extent of the path +func (s *ScannerGV) GetPathExtent() fixed.Rectangle26_6 { + return fixed.Rectangle26_6{Min: fixed.Point26_6{X: s.minX, Y: s.minY}, Max: fixed.Point26_6{X: s.maxX, Y: s.maxY}} +} + +// At returns the color of the ClipImage at the point x,y +func (c *ClipImage) At(x, y int) color.Color { + p := image.Point{x, y} + if p.In(c.clip) { + return c.ColorFuncImage.At(x, y) + } + return noApha +} + +// SetWinding set the winding rule for the scanner +func (s *ScannerGV) SetWinding(useNonZeroWinding bool) { + // no-op as scanner gv does not support even-odd winding +} + +// SetColor set the color type for the scanner +func (s *ScannerGV) SetColor(clr interface{}) { + switch c := clr.(type) { + case color.Color: + s.clipImage.ColorFuncImage.Uniform.C = c + if s.clipImage.clip == image.ZR { + s.Source = &s.clipImage.ColorFuncImage.Uniform + } else { + s.clipImage.ColorFuncImage.colorFunc = func(x, y int) color.Color { + return c + } + s.Source = s.clipImage + } + case ColorFunc: + s.clipImage.ColorFuncImage.colorFunc = c + if s.clipImage.clip == image.ZR { + s.Source = &s.clipImage.ColorFuncImage + } else { + s.Source = s.clipImage + } + } +} + +// SetClip sets an optional clipping rectangle to restrict rendering only to +// that region -- if size is 0 then ignored (set to image.ZR to clear) +func (s *ScannerGV) SetClip(rect image.Rectangle) { + s.clipImage.clip = rect + if s.Source == &s.clipImage.ColorFuncImage.Uniform { + s.SetColor(s.clipImage.ColorFuncImage.Uniform.C) + } else { + s.SetColor(s.clipImage.ColorFuncImage.colorFunc) + } +} + +func (s *ScannerGV) set(a fixed.Point26_6) { + if s.maxX < a.X { + s.maxX = a.X + } + if s.maxY < a.Y { + s.maxY = a.Y + } + if s.minX > a.X { + s.minX = a.X + } + if s.minY > a.Y { + s.minY = a.Y + } +} + +// Start starts a new path at the given point. +func (s *ScannerGV) Start(a fixed.Point26_6) { + s.set(a) + s.r.MoveTo(float32(a.X)/64, float32(a.Y)/64) +} + +// Line adds a linear segment to the current curve. +func (s *ScannerGV) Line(b fixed.Point26_6) { + s.set(b) + s.r.LineTo(float32(b.X)/64, float32(b.Y)/64) +} + +// Draw renders the accumulate scan to the desination +func (s *ScannerGV) Draw() { + // This draws the entire bounds of the image, because + // at this point the alpha mask does not shift with the + // placement of the target rectangle in the vector rasterizer + s.r.Draw(s.Dest, s.Dest.Bounds(), s.Source, s.Offset) + + // Remove the line above and uncomment the lines below if you + // are using a version of the vector rasterizer that shifts the alpha + // mask with the placement of the target + + // s.Targ.Min.X = int(s.minX >> 6) + // s.Targ.Min.Y = int(s.minY >> 6) + // s.Targ.Max.X = int(s.maxX>>6) + 1 + // s.Targ.Max.Y = int(s.maxY>>6) + 1 + // s.Targ = s.Targ.Intersect(s.Dest.Bounds()) // This check should be done by the rasterizer? + // s.r.Draw(s.Dest, s.Targ, s.Source, s.Offset) +} + +// Clear cancels any previous accumulated scans +func (s *ScannerGV) Clear() { + p := s.r.Size() + s.r.Reset(p.X, p.Y) + const mxfi = fixed.Int26_6(math.MaxInt32) + s.minX, s.minY, s.maxX, s.maxY = mxfi, mxfi, -mxfi, -mxfi +} + +// SetBounds sets the maximum width and height of the rasterized image and +// calls Clear. The width and height are in pixels, not fixed.Int26_6 units. +func (s *ScannerGV) SetBounds(width, height int) { + s.r.Reset(width, height) +} + +// NewScannerGV creates a new Scanner with the given bounds. +func NewScannerGV(width, height int, dest draw.Image, + targ image.Rectangle) *ScannerGV { + s := new(ScannerGV) + s.SetBounds(width, height) + s.Dest = dest + s.Targ = targ + s.clipImage = &ClipImage{} + s.clipImage.ColorFuncImage.Uniform.C = &color.RGBA{255, 0, 0, 255} + s.Source = &s.clipImage.ColorFuncImage.Uniform + s.Offset = image.Point{0, 0} + return s +} diff --git a/vendor/github.com/srwiley/rasterx/shapes.go b/vendor/github.com/srwiley/rasterx/shapes.go new file mode 100644 index 0000000000000..d5beaefc62616 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/shapes.go @@ -0,0 +1,221 @@ +// Copyright 2018 by the rasterx Authors. All rights reserved. +//_ +// created: 2/06/2018 by S.R.Wiley +// Functions that rasterize common shapes easily. + +package rasterx + +import ( + "math" + + "golang.org/x/image/math/fixed" +) + +// MaxDx is the Maximum radians a cubic splice is allowed to span +// in ellipse parametric when approximating an off-axis ellipse. +const MaxDx float64 = math.Pi / 8 + +// ToFixedP converts two floats to a fixed point. +func ToFixedP(x, y float64) (p fixed.Point26_6) { + p.X = fixed.Int26_6(x * 64) + p.Y = fixed.Int26_6(y * 64) + return +} + +// AddCircle adds a circle to the Adder p +func AddCircle(cx, cy, r float64, p Adder) { + AddEllipse(cx, cy, r, r, 0, p) +} + +// AddEllipse adds an elipse with center at cx,cy, with the indicated +// x and y radius, (rx, ry), rotated around the center by rot degrees. +func AddEllipse(cx, cy, rx, ry, rot float64, p Adder) { + rotRads := rot * math.Pi / 180 + px, py := Identity. + Translate(cx, cy).Rotate(rotRads).Translate(-cx, -cy).Transform(cx+rx, cy) + points := []float64{rx, ry, rot, 1.0, 0.0, px, py} + p.Start(ToFixedP(px, py)) + AddArc(points, cx, cy, px, py, p) + p.Stop(true) +} + +// AddRect adds a rectangle of the indicated size, rotated +// around the center by rot degrees. +func AddRect(minX, minY, maxX, maxY, rot float64, p Adder) { + rot *= math.Pi / 180 + cx, cy := (minX+maxX)/2, (minY+maxY)/2 + m := Identity.Translate(cx, cy).Rotate(rot).Translate(-cx, -cy) + q := &MatrixAdder{M: m, Adder: p} + q.Start(ToFixedP(minX, minY)) + q.Line(ToFixedP(maxX, minY)) + q.Line(ToFixedP(maxX, maxY)) + q.Line(ToFixedP(minX, maxY)) + q.Stop(true) +} + +// AddRoundRect adds a rectangle of the indicated size, rotated +// around the center by rot degrees with rounded corners of radius +// rx in the x axis and ry in the y axis. gf specifes the shape of the +// filleting function. Valid values are RoundGap, QuadraticGap, CubicGap, +// FlatGap, or nil which defaults to a flat gap. +func AddRoundRect(minX, minY, maxX, maxY, rx, ry, rot float64, gf GapFunc, p Adder) { + if rx <= 0 || ry <= 0 { + AddRect(minX, minY, maxX, maxY, rot, p) + return + } + rot *= math.Pi / 180 + if gf == nil { + gf = FlatGap + } + w := maxX - minX + if w < rx*2 { + rx = w / 2 + } + h := maxY - minY + if h < ry*2 { + ry = h / 2 + } + stretch := rx / ry + midY := minY + h/2 + m := Identity.Translate(minX+w/2, midY).Rotate(rot).Scale(1, 1/stretch).Translate(-minX-w/2, -minY-h/2) + maxY = midY + h/2*stretch + minY = midY - h/2*stretch + + q := &MatrixAdder{M: m, Adder: p} + + q.Start(ToFixedP(minX+rx, minY)) + q.Line(ToFixedP(maxX-rx, minY)) + gf(q, ToFixedP(maxX-rx, minY+rx), ToFixedP(0, -rx), ToFixedP(rx, 0)) + q.Line(ToFixedP(maxX, maxY-rx)) + gf(q, ToFixedP(maxX-rx, maxY-rx), ToFixedP(rx, 0), ToFixedP(0, rx)) + q.Line(ToFixedP(minX+rx, maxY)) + gf(q, ToFixedP(minX+rx, maxY-rx), ToFixedP(0, rx), ToFixedP(-rx, 0)) + q.Line(ToFixedP(minX, minY+rx)) + gf(q, ToFixedP(minX+rx, minY+rx), ToFixedP(-rx, 0), ToFixedP(0, -rx)) + q.Stop(true) +} + +//AddArc adds an arc to the adder p +func AddArc(points []float64, cx, cy, px, py float64, p Adder) (lx, ly float64) { + rotX := points[2] * math.Pi / 180 // Convert degress to radians + largeArc := points[3] != 0 + sweep := points[4] != 0 + startAngle := math.Atan2(py-cy, px-cx) - rotX + endAngle := math.Atan2(points[6]-cy, points[5]-cx) - rotX + deltaTheta := endAngle - startAngle + arcBig := math.Abs(deltaTheta) > math.Pi + + // Approximate ellipse using cubic bezeir splines + etaStart := math.Atan2(math.Sin(startAngle)/points[1], math.Cos(startAngle)/points[0]) + etaEnd := math.Atan2(math.Sin(endAngle)/points[1], math.Cos(endAngle)/points[0]) + deltaEta := etaEnd - etaStart + if (arcBig && !largeArc) || (!arcBig && largeArc) { // Go has no boolean XOR + if deltaEta < 0 { + deltaEta += math.Pi * 2 + } else { + deltaEta -= math.Pi * 2 + } + } + // This check might be needed if the center point of the elipse is + // at the midpoint of the start and end lines. + if deltaEta < 0 && sweep { + deltaEta += math.Pi * 2 + } else if deltaEta >= 0 && !sweep { + deltaEta -= math.Pi * 2 + } + + // Round up to determine number of cubic splines to approximate bezier curve + segs := int(math.Abs(deltaEta)/MaxDx) + 1 + dEta := deltaEta / float64(segs) // span of each segment + // Approximate the ellipse using a set of cubic bezier curves by the method of + // L. Maisonobe, "Drawing an elliptical arc using polylines, quadratic + // or cubic Bezier curves", 2003 + // https://www.spaceroots.org/documents/elllipse/elliptical-arc.pdf + tde := math.Tan(dEta / 2) + alpha := math.Sin(dEta) * (math.Sqrt(4+3*tde*tde) - 1) / 3 // Math is fun! + lx, ly = px, py + sinTheta, cosTheta := math.Sin(rotX), math.Cos(rotX) + ldx, ldy := ellipsePrime(points[0], points[1], sinTheta, cosTheta, etaStart, cx, cy) + for i := 1; i <= segs; i++ { + eta := etaStart + dEta*float64(i) + var px, py float64 + if i == segs { + px, py = points[5], points[6] // Just makes the end point exact; no roundoff error + } else { + px, py = ellipsePointAt(points[0], points[1], sinTheta, cosTheta, eta, cx, cy) + } + dx, dy := ellipsePrime(points[0], points[1], sinTheta, cosTheta, eta, cx, cy) + p.CubeBezier(ToFixedP(lx+alpha*ldx, ly+alpha*ldy), + ToFixedP(px-alpha*dx, py-alpha*dy), ToFixedP(px, py)) + lx, ly, ldx, ldy = px, py, dx, dy + } + return lx, ly +} + +// ellipsePrime gives tangent vectors for parameterized elipse; a, b, radii, eta parameter, center cx, cy +func ellipsePrime(a, b, sinTheta, cosTheta, eta, cx, cy float64) (px, py float64) { + bCosEta := b * math.Cos(eta) + aSinEta := a * math.Sin(eta) + px = -aSinEta*cosTheta - bCosEta*sinTheta + py = -aSinEta*sinTheta + bCosEta*cosTheta + return +} + +// ellipsePointAt gives points for parameterized elipse; a, b, radii, eta parameter, center cx, cy +func ellipsePointAt(a, b, sinTheta, cosTheta, eta, cx, cy float64) (px, py float64) { + aCosEta := a * math.Cos(eta) + bSinEta := b * math.Sin(eta) + px = cx + aCosEta*cosTheta - bSinEta*sinTheta + py = cy + aCosEta*sinTheta + bSinEta*cosTheta + return +} + +// FindEllipseCenter locates the center of the Ellipse if it exists. If it does not exist, +// the radius values will be increased minimally for a solution to be possible +// while preserving the ra to rb ratio. ra and rb arguments are pointers that can be +// checked after the call to see if the values changed. This method uses coordinate transformations +// to reduce the problem to finding the center of a circle that includes the origin +// and an arbitrary point. The center of the circle is then transformed +// back to the original coordinates and returned. +func FindEllipseCenter(ra, rb *float64, rotX, startX, startY, endX, endY float64, sweep, smallArc bool) (cx, cy float64) { + cos, sin := math.Cos(rotX), math.Sin(rotX) + + // Move origin to start point + nx, ny := endX-startX, endY-startY + + // Rotate ellipse x-axis to coordinate x-axis + nx, ny = nx*cos+ny*sin, -nx*sin+ny*cos + // Scale X dimension so that ra = rb + nx *= *rb / *ra // Now the ellipse is a circle radius rb; therefore foci and center coincide + + midX, midY := nx/2, ny/2 + midlenSq := midX*midX + midY*midY + + var hr float64 + if *rb**rb < midlenSq { + // Requested ellipse does not exist; scale ra, rb to fit. Length of + // span is greater than max width of ellipse, must scale *ra, *rb + nrb := math.Sqrt(midlenSq) + if *ra == *rb { + *ra = nrb // prevents roundoff + } else { + *ra = *ra * nrb / *rb + } + *rb = nrb + } else { + hr = math.Sqrt(*rb**rb-midlenSq) / math.Sqrt(midlenSq) + } + // Notice that if hr is zero, both answers are the same. + if (sweep && smallArc) || (!sweep && !smallArc) { + cx = midX + midY*hr + cy = midY - midX*hr + } else { + cx = midX - midY*hr + cy = midY + midX*hr + } + + // reverse scale + cx *= *ra / *rb + //Reverse rotate and translate back to original coordinates + return cx*cos - cy*sin + startX, cx*sin + cy*cos + startY +} diff --git a/vendor/github.com/srwiley/rasterx/stroke.go b/vendor/github.com/srwiley/rasterx/stroke.go new file mode 100644 index 0000000000000..1b14b59c1d8c7 --- /dev/null +++ b/vendor/github.com/srwiley/rasterx/stroke.go @@ -0,0 +1,677 @@ +// Copyright 2017 by the rasterx Authors. All rights reserved. +// +// created: 2017 by S.R.Wiley + +package rasterx + +import ( + "math" + + "golang.org/x/image/math/fixed" +) + +const ( + cubicsPerHalfCircle = 8 // Number of cubic beziers to approx half a circle + epsilonFixed = fixed.Int26_6(16) // 1/4 in fixed point + // fixed point t paramaterization shift factor; + // (2^this)/64 is the max length of t for fixed.Int26_6 + tStrokeShift = 14 +) + +type ( + // JoinMode type to specify how segments join. + JoinMode uint8 + // CapFunc defines a function that draws caps on the ends of lines + CapFunc func(p Adder, a, eNorm fixed.Point26_6) + // GapFunc defines a function to bridge gaps when the miter limit is + // exceeded + GapFunc func(p Adder, a, tNorm, lNorm fixed.Point26_6) + + // C2Point represents a point that connects two stroke segments + // and holds the tangent, normal and radius of curvature + // of the trailing and leading segments in fixed point values. + C2Point struct { + P, TTan, LTan, TNorm, LNorm fixed.Point26_6 + RT, RL fixed.Int26_6 + } + + // Stroker does everything a Filler does, but + // also allows for stroking and dashed stroking in addition to + // filling + Stroker struct { + Filler + CapT, CapL CapFunc // Trailing and leading cap funcs may be set separately + JoinGap GapFunc // When gap appears between segments, this function is called + + firstP, trailPoint, leadPoint C2Point // Tracks progress of the stroke + ln fixed.Point26_6 // last normal of intra-seg connection. + u, mLimit fixed.Int26_6 // u is the half-width of the stroke. + + JoinMode JoinMode + inStroke bool + } +) + +// JoinMode constants determine how stroke segments bridge the gap at a join +// ArcClip mode is like MiterClip applied to arcs, and is not part of the SVG2.0 +// standard. +const ( + Arc JoinMode = iota + ArcClip + Miter + MiterClip + Bevel + Round +) + +// NewStroker returns a ptr to a Stroker with default values. +// A Stroker has all of the capabilities of a Filler and Scanner, plus the ability +// to stroke curves with solid lines. Use SetStroke to configure with non-default +// values. +func NewStroker(width, height int, scanner Scanner) *Stroker { + r := new(Stroker) + r.Scanner = scanner + r.SetBounds(width, height) + //Defaults for stroking + r.SetWinding(true) + r.u = 2 << 6 + r.mLimit = 4 << 6 + r.JoinMode = MiterClip + r.JoinGap = RoundGap + r.CapL = RoundCap + r.CapT = RoundCap + r.SetStroke(1<<6, 4<<6, ButtCap, nil, FlatGap, MiterClip) + return r +} + +// SetStroke set the parameters for stroking a line. width is the width of the line, miterlimit is the miter cutoff +// value for miter, arc, miterclip and arcClip joinModes. CapL and CapT are the capping functions for leading and trailing +// line ends. If one is nil, the other function is used at both ends. If both are nil, both ends are ButtCapped. +// gp is the gap function that determines how a gap on the convex side of two joining lines is filled. jm is the JoinMode +// for curve segments. +func (r *Stroker) SetStroke(width, miterLimit fixed.Int26_6, capL, capT CapFunc, gp GapFunc, jm JoinMode) { + r.u = width / 2 + r.CapL = capL + r.CapT = capT + r.JoinMode = jm + r.JoinGap = gp + r.mLimit = (r.u * miterLimit) >> 6 + + if r.CapT == nil { + if r.CapL == nil { + r.CapT = ButtCap + } else { + r.CapT = r.CapL + } + } + if r.CapL == nil { + r.CapL = r.CapT + } + if gp == nil { + if r.JoinMode == Round { + r.JoinGap = RoundGap + } else { + r.JoinGap = FlatGap + } + } + +} + +// GapToCap is a utility that converts a CapFunc to GapFunc +func GapToCap(p Adder, a, eNorm fixed.Point26_6, gf GapFunc) { + p.Start(a.Add(eNorm)) + gf(p, a, eNorm, Invert(eNorm)) + p.Line(a.Sub(eNorm)) +} + +var ( + // ButtCap caps lines with a straight line + ButtCap CapFunc = func(p Adder, a, eNorm fixed.Point26_6) { + p.Start(a.Add(eNorm)) + p.Line(a.Sub(eNorm)) + } + // SquareCap caps lines with a square which is slightly longer than ButtCap + SquareCap CapFunc = func(p Adder, a, eNorm fixed.Point26_6) { + tpt := a.Add(turnStarboard90(eNorm)) + p.Start(a.Add(eNorm)) + p.Line(tpt.Add(eNorm)) + p.Line(tpt.Sub(eNorm)) + p.Line(a.Sub(eNorm)) + } + // RoundCap caps lines with a half-circle + RoundCap CapFunc = func(p Adder, a, eNorm fixed.Point26_6) { + GapToCap(p, a, eNorm, RoundGap) + } + // CubicCap caps lines with a cubic bezier + CubicCap CapFunc = func(p Adder, a, eNorm fixed.Point26_6) { + GapToCap(p, a, eNorm, CubicGap) + } + // QuadraticCap caps lines with a quadratic bezier + QuadraticCap CapFunc = func(p Adder, a, eNorm fixed.Point26_6) { + GapToCap(p, a, eNorm, QuadraticGap) + } + // Gap functions + + //FlatGap bridges miter-limit gaps with a straight line + FlatGap GapFunc = func(p Adder, a, tNorm, lNorm fixed.Point26_6) { + p.Line(a.Add(lNorm)) + } + // RoundGap bridges miter-limit gaps with a circular arc + RoundGap GapFunc = func(p Adder, a, tNorm, lNorm fixed.Point26_6) { + strokeArc(p, a, a.Add(tNorm), a.Add(lNorm), true, 0, 0, p.Line) + p.Line(a.Add(lNorm)) // just to be sure line joins cleanly, + // last pt in stoke arc may not be precisely s2 + } + // CubicGap bridges miter-limit gaps with a cubic bezier + CubicGap GapFunc = func(p Adder, a, tNorm, lNorm fixed.Point26_6) { + p.CubeBezier(a.Add(tNorm).Add(turnStarboard90(tNorm)), a.Add(lNorm).Add(turnPort90(lNorm)), a.Add(lNorm)) + } + // QuadraticGap bridges miter-limit gaps with a quadratic bezier + QuadraticGap GapFunc = func(p Adder, a, tNorm, lNorm fixed.Point26_6) { + c1, c2 := a.Add(tNorm).Add(turnStarboard90(tNorm)), a.Add(lNorm).Add(turnPort90(lNorm)) + cm := c1.Add(c2).Mul(fixed.Int26_6(1 << 5)) + p.QuadBezier(cm, a.Add(lNorm)) + } +) + +// StrokeArc strokes a circular arc by approximation with bezier curves +func strokeArc(p Adder, a, s1, s2 fixed.Point26_6, clockwise bool, trimStart, + trimEnd fixed.Int26_6, firstPoint func(p fixed.Point26_6)) (ps1, ds1, ps2, ds2 fixed.Point26_6) { + // Approximate the circular arc using a set of cubic bezier curves by the method of + // L. Maisonobe, "Drawing an elliptical arc using polylines, quadratic + // or cubic Bezier curves", 2003 + // https://www.spaceroots.org/documents/elllipse/elliptical-arc.pdf + // The method was simplified for circles. + theta1 := math.Atan2(float64(s1.Y-a.Y), float64(s1.X-a.X)) + theta2 := math.Atan2(float64(s2.Y-a.Y), float64(s2.X-a.X)) + if !clockwise { + for theta1 < theta2 { + theta1 += math.Pi * 2 + } + } else { + for theta2 < theta1 { + theta2 += math.Pi * 2 + } + } + deltaTheta := theta2 - theta1 + if trimStart > 0 { + ds := (deltaTheta * float64(trimStart)) / float64(1< 0 { + ds := (deltaTheta * float64(trimEnd)) / float64(1< -epsilonFixed*epsilonFixed { // Almost co-linear or convex + ra.Line(s2) + return // No need to fill any gaps + } + + var ct, cl fixed.Point26_6 // Center of curvature trailing, leading + var rt, rl fixed.Int26_6 // Radius of curvature trailing, leading + + // Adjust radiuses for stroke width + if r.JoinMode == Arc || r.JoinMode == ArcClip { + // Find centers of radius of curvature and adjust the radius to be drawn + // by half the stroke width. + if p.RT != 0 { + if p.RT > 0 { + ct = p.P.Add(ToLength(turnPort90(p.TTan), p.RT)) + rt = p.RT - r.u + } else { + ct = p.P.Sub(ToLength(turnPort90(p.TTan), -p.RT)) + rt = -p.RT + r.u + } + if rt < 0 { + rt = 0 + } + } + if p.RL != 0 { + if p.RL > 0 { + cl = p.P.Add(ToLength(turnPort90(p.LTan), p.RL)) + rl = p.RL - r.u + } else { + cl = p.P.Sub(ToLength(turnPort90(p.LTan), -p.RL)) + rl = -p.RL + r.u + } + if rl < 0 { + rl = 0 + } + } + } + + if r.JoinMode == MiterClip || r.JoinMode == Miter || + // Arc or ArcClip with 0 tRadCurve and 0 lRadCurve is treated the same as a + // Miter or MiterClip join, resp. + ((r.JoinMode == Arc || r.JoinMode == ArcClip) && (rt == 0 && rl == 0)) { + xt := CalcIntersect(s1.Sub(p.TTan), s1, s2, s2.Sub(p.LTan)) + xa := xt.Sub(p.P) + if Length(xa) < r.mLimit { // within miter limit + ra.Line(xt) + ra.Line(s2) + return + } + if r.JoinMode == MiterClip || (r.JoinMode == ArcClip) { + //Projection of tNorm onto xa + tProjP := xa.Mul(fixed.Int26_6((DotProd(xa, p.TNorm) << 6) / DotProd(xa, xa))) + projLen := Length(tProjP) + if r.mLimit > projLen { // the miter limit line is past the bevel point + // t is the fraction shifted by tStrokeShift to scale the vectors from the bevel point + // to the line intersection, so that they abbut the miter limit line. + tiLength := Length(xa) + sx1, sx2 := xt.Sub(s1), xt.Sub(s2) + t := (r.mLimit - projLen) << tStrokeShift / (tiLength - projLen) + tx := ToLength(sx1, t*Length(sx1)>>tStrokeShift) + lx := ToLength(sx2, t*Length(sx2)>>tStrokeShift) + vx := ToLength(xa, t*Length(xa)>>tStrokeShift) + s1p, _, ap := s1.Add(tx), s2.Add(lx), p.P.Add(vx) + gLen := Length(ap.Sub(s1p)) + ra.Line(s1p) + r.JoinGap(ra, ap, ToLength(turnPort90(p.TTan), gLen), ToLength(turnPort90(p.LTan), gLen)) + ra.Line(s2) + return + } + } // Fallthrough + } else if r.JoinMode == Arc || r.JoinMode == ArcClip { + // Test for cases of a bezier meeting line, an line meeting a bezier, + // or a bezier meeting a bezier. (Line meeting line is handled above.) + switch { + case rt == 0: // rl != 0, because one must be non-zero as checked above + xt, intersect := RayCircleIntersection(s1.Add(p.TTan), s1, cl, rl) + if intersect { + ray1, ray2 := xt.Sub(cl), s2.Sub(cl) + clockwise := (ray1.X*ray2.Y > ray1.Y*ray2.X) // Sign of xprod + if Length(p.P.Sub(xt)) < r.mLimit { // within miter limit + strokeArc(ra, cl, xt, s2, clockwise, 0, 0, ra.Line) + ra.Line(s2) + return + } + // Not within miter limit line + if r.JoinMode == ArcClip { // Scale bevel points towards xt, and call gap func + xa := xt.Sub(p.P) + //Projection of tNorm onto xa + tProjP := xa.Mul(fixed.Int26_6((DotProd(xa, p.TNorm) << 6) / DotProd(xa, xa))) + projLen := Length(tProjP) + if r.mLimit > projLen { // the miter limit line is past the bevel point + // t is the fraction shifted by tStrokeShift to scale the line or arc from the bevel point + // to the line intersection, so that they abbut the miter limit line. + sx1 := xt.Sub(s1) //, xt.Sub(s2) + t := fixed.Int26_6(1<>tStrokeShift) + s1p := xt.Sub(tx) + ra.Line(s1p) + sp1, ds1, ps2, _ := strokeArc(ra, cl, xt, s2, clockwise, t, 0, ra.Start) + ra.Start(s1p) + // calc gap center as pt where -tnorm and line perp to midcoord + midP := sp1.Add(s1p).Mul(fixed.Int26_6(1 << 5)) // midpoint + midLine := turnPort90(midP.Sub(sp1)) + if midLine.X*midLine.X+midLine.Y*midLine.Y > epsilonFixed { // if midline is zero, CalcIntersect is invalid + ap := CalcIntersect(s1p, s1p.Sub(p.TNorm), midLine.Add(midP), midP) + gLen := Length(ap.Sub(s1p)) + if clockwise { + ds1 = Invert(ds1) + } + r.JoinGap(ra, ap, ToLength(turnPort90(p.TTan), gLen), ToLength(turnStarboard90(ds1), gLen)) + } + ra.Line(sp1) + ra.Start(ps2) + ra.Line(s2) + return + } + //Bevel points not past miter limit: fallthrough + } + } + case rl == 0: // rt != 0, because one must be non-zero as checked above + xt, intersect := RayCircleIntersection(s2.Sub(p.LTan), s2, ct, rt) + if intersect { + ray1, ray2 := s1.Sub(ct), xt.Sub(ct) + clockwise := ray1.X*ray2.Y > ray1.Y*ray2.X + if Length(p.P.Sub(xt)) < r.mLimit { // within miter limit + strokeArc(ra, ct, s1, xt, clockwise, 0, 0, ra.Line) + ra.Line(s2) + return + } + // Not within miter limit line + if r.JoinMode == ArcClip { // Scale bevel points towards xt, and call gap func + xa := xt.Sub(p.P) + //Projection of lNorm onto xa + lProjP := xa.Mul(fixed.Int26_6((DotProd(xa, p.LNorm) << 6) / DotProd(xa, xa))) + projLen := Length(lProjP) + if r.mLimit > projLen { // The miter limit line is past the bevel point, + // t is the fraction to scale the line or arc from the bevel point + // to the line intersection, so that they abbut the miter limit line. + sx2 := xt.Sub(s2) + t := fixed.Int26_6(1<>tStrokeShift) + s2p := xt.Sub(lx) + _, _, ps2, ds2 := strokeArc(ra, ct, s1, xt, clockwise, 0, t, ra.Line) + // calc gap center as pt where -lnorm and line perp to midcoord + midP := s2p.Add(ps2).Mul(fixed.Int26_6(1 << 5)) // midpoint + midLine := turnStarboard90(midP.Sub(ps2)) + if midLine.X*midLine.X+midLine.Y*midLine.Y > epsilonFixed { // if midline is zero, CalcIntersect is invalid + ap := CalcIntersect(midP, midLine.Add(midP), s2p, s2p.Sub(p.LNorm)) + gLen := Length(ap.Sub(ps2)) + if clockwise { + ds2 = Invert(ds2) + } + r.JoinGap(ra, ap, ToLength(turnStarboard90(ds2), gLen), ToLength(turnPort90(p.LTan), gLen)) + } + ra.Line(s2) + return + } + //Bevel points not past miter limit: fallthrough + } + } + default: // Both rl != 0 and rt != 0 as checked above + xt1, xt2, gIntersect := CircleCircleIntersection(ct, cl, rt, rl) + xt, intersect := ClosestPortside(s1, s2, xt1, xt2, gIntersect) + if intersect { + ray1, ray2 := s1.Sub(ct), xt.Sub(ct) + clockwiseT := (ray1.X*ray2.Y > ray1.Y*ray2.X) + ray1, ray2 = xt.Sub(cl), s2.Sub(cl) + clockwiseL := ray1.X*ray2.Y > ray1.Y*ray2.X + + if Length(p.P.Sub(xt)) < r.mLimit { // within miter limit + strokeArc(ra, ct, s1, xt, clockwiseT, 0, 0, ra.Line) + strokeArc(ra, cl, xt, s2, clockwiseL, 0, 0, ra.Line) + ra.Line(s2) + return + } + + if r.JoinMode == ArcClip { // Scale bevel points towards xt, and call gap func + xa := xt.Sub(p.P) + //Projection of lNorm onto xa + lProjP := xa.Mul(fixed.Int26_6((DotProd(xa, p.LNorm) << 6) / DotProd(xa, xa))) + projLen := Length(lProjP) + if r.mLimit > projLen { // The miter limit line is past the bevel point, + // t is the fraction to scale the line or arc from the bevel point + // to the line intersection, so that they abbut the miter limit line. + t := fixed.Int26_6(1< epsilonFixed { // if midline is zero, CalcIntersect is invalid + if clockwiseT { + ds1 = Invert(ds1) + } + if clockwiseL { + ds2 = Invert(ds2) + } + ap := CalcIntersect(midP, midLine.Add(midP), ps2, ps2.Sub(turnStarboard90(ds2))) + gLen := Length(ap.Sub(ps2)) + r.JoinGap(ra, ap, ToLength(turnStarboard90(ds1), gLen), ToLength(turnStarboard90(ds2), gLen)) + } + ra.Line(ps2) + ra.Start(fs2) + ra.Line(s2) + return + } + } + } + // fallthrough to final JoinGap + } + } + r.JoinGap(ra, p.P, p.TNorm, p.LNorm) + ra.Line(s2) + return +} + +// Stop a stroked line. The line will close +// is isClosed is true. Otherwise end caps will +// be drawn at both ends. +func (r *Stroker) Stop(isClosed bool) { + if r.inStroke == false { + return + } + rf := &r.Filler + if isClosed { + if r.firstP.P != rf.a { + r.Line(r.firstP.P) + } + a := rf.a + r.firstP.TNorm = r.leadPoint.TNorm + r.firstP.RT = r.leadPoint.RT + r.firstP.TTan = r.leadPoint.TTan + + rf.Start(r.firstP.P.Sub(r.firstP.TNorm)) + rf.Line(a.Sub(r.ln)) + rf.Start(a.Add(r.ln)) + rf.Line(r.firstP.P.Add(r.firstP.TNorm)) + r.Joiner(r.firstP) + r.firstP.blackWidowMark(rf) + } else { + a := rf.a + rf.Start(r.leadPoint.P.Sub(r.leadPoint.TNorm)) + rf.Line(a.Sub(r.ln)) + rf.Start(a.Add(r.ln)) + rf.Line(r.leadPoint.P.Add(r.leadPoint.TNorm)) + r.CapL(rf, r.leadPoint.P, r.leadPoint.TNorm) + r.CapT(rf, r.firstP.P, Invert(r.firstP.LNorm)) + } + r.inStroke = false +} + +// QuadBezier starts a stroked quadratic bezier. +func (r *Stroker) QuadBezier(b, c fixed.Point26_6) { + r.quadBezierf(r, b, c) +} + +// CubeBezier starts a stroked quadratic bezier. +func (r *Stroker) CubeBezier(b, c, d fixed.Point26_6) { + r.cubeBezierf(r, b, c, d) +} + +// quadBezierf calcs end curvature of beziers +func (r *Stroker) quadBezierf(s Rasterx, b, c fixed.Point26_6) { + r.trailPoint = r.leadPoint + r.CalcEndCurvature(r.a, b, c, c, b, r.a, fixed.Int52_12(2<<12), doCalcCurvature(s)) + r.QuadBezierF(s, b, c) + r.a = c +} + +// doCalcCurvature determines if calculation of the end curvature is required +// depending on the raster type and JoinMode +func doCalcCurvature(r Rasterx) bool { + switch q := r.(type) { + case *Filler: + return false // never for filler + case *Stroker: + return (q.JoinMode == Arc || q.JoinMode == ArcClip) + case *Dasher: + return (q.JoinMode == Arc || q.JoinMode == ArcClip) + default: + return true // Better safe than sorry if another raster type is used + } +} + +func (r *Stroker) cubeBezierf(sgm Rasterx, b, c, d fixed.Point26_6) { + if (r.a == b && c == d) || (r.a == b && b == c) || (c == b && d == c) { + sgm.Line(d) + return + } + r.trailPoint = r.leadPoint + // Only calculate curvature if stroking or and using arc or arc-clip + doCalcCurve := doCalcCurvature(sgm) + const dm = fixed.Int52_12((3 << 12) / 2) + switch { + // b != c, and c != d see above + case r.a == b: + r.CalcEndCurvature(b, c, d, d, c, b, dm, doCalcCurve) + // b != a, and b != c, see above + case c == d: + r.CalcEndCurvature(r.a, b, c, c, b, r.a, dm, doCalcCurve) + default: + r.CalcEndCurvature(r.a, b, c, d, c, b, dm, doCalcCurve) + } + r.CubeBezierF(sgm, b, c, d) + r.a = d +} + +// Line adds a line segment to the rasterizer +func (r *Stroker) Line(b fixed.Point26_6) { + r.LineSeg(r, b) +} + +//LineSeg is called by both the Stroker and Dasher +func (r *Stroker) LineSeg(sgm Rasterx, b fixed.Point26_6) { + r.trailPoint = r.leadPoint + ba := b.Sub(r.a) + if ba.X == 0 && ba.Y == 0 { // a == b, line is degenerate + if r.trailPoint.TTan.X != 0 || r.trailPoint.TTan.Y != 0 { + ba = r.trailPoint.TTan // Use last tangent for seg tangent + } else { // Must be on top of last moveto; set ba to X axis unit vector + ba = fixed.Point26_6{X: 1 << 6, Y: 0} + } + } + bnorm := turnPort90(ToLength(ba, r.u)) + r.trailPoint.LTan = ba + r.leadPoint.TTan = ba + r.trailPoint.LNorm = bnorm + r.leadPoint.TNorm = bnorm + r.trailPoint.RL = 0.0 + r.leadPoint.RT = 0.0 + r.trailPoint.P = r.a + r.leadPoint.P = b + + sgm.joinF() + sgm.lineF(b) + r.a = b +} + +// lineF is for intra-curve lines. It is required for the Rasterizer interface +// so that if the line is being stroked or dash stroked, different actions can be +// taken. +func (r *Stroker) lineF(b fixed.Point26_6) { + // b is either an intra-segment value, or + // the end of the segment. + var bnorm fixed.Point26_6 + a := r.a // Hold a since r.a is going to change during stroke operation + if b == r.leadPoint.P { // End of segment + bnorm = r.leadPoint.TNorm // Use more accurate leadPoint tangent + } else { + bnorm = turnPort90(ToLength(b.Sub(a), r.u)) // Intra segment normal + } + ra := &r.Filler + ra.Start(b.Sub(bnorm)) + ra.Line(a.Sub(r.ln)) + ra.Start(a.Add(r.ln)) + ra.Line(b.Add(bnorm)) + r.a = b + r.ln = bnorm +} + +// Start iniitates a stroked path +func (r *Stroker) Start(a fixed.Point26_6) { + r.inStroke = false + r.Filler.Start(a) +} + +// CalcEndCurvature calculates the radius of curvature given the control points +// of a bezier curve. +// It is a low level function exposed for the purposes of callbacks +// and debugging. +func (r *Stroker) CalcEndCurvature(p0, p1, p2, q0, q1, q2 fixed.Point26_6, + dm fixed.Int52_12, calcRadCuve bool) { + r.trailPoint.P = p0 + r.leadPoint.P = q0 + r.trailPoint.LTan = p1.Sub(p0) + r.leadPoint.TTan = q0.Sub(q1) + r.trailPoint.LNorm = turnPort90(ToLength(r.trailPoint.LTan, r.u)) + r.leadPoint.TNorm = turnPort90(ToLength(r.leadPoint.TTan, r.u)) + if calcRadCuve { + r.trailPoint.RL = RadCurvature(p0, p1, p2, dm) + r.leadPoint.RT = -RadCurvature(q0, q1, q2, dm) + } else { + r.trailPoint.RL = 0 + r.leadPoint.RT = 0 + } +} + +func (r *Stroker) joinF() { + if r.inStroke == false { + r.inStroke = true + r.firstP = r.trailPoint + } else { + ra := &r.Filler + tl := r.trailPoint.P.Sub(r.trailPoint.TNorm) + th := r.trailPoint.P.Add(r.trailPoint.TNorm) + if r.a != r.trailPoint.P || r.ln != r.trailPoint.TNorm { + a := r.a + ra.Start(tl) + ra.Line(a.Sub(r.ln)) + ra.Start(a.Add(r.ln)) + ra.Line(th) + } + r.Joiner(r.trailPoint) + r.trailPoint.blackWidowMark(ra) + } + r.ln = r.trailPoint.LNorm + r.a = r.trailPoint.P +} + +// blackWidowMark handles a gap in a stroke that can occur when a line end is too close +// to a segment to segment join point. Although it is only required in those cases, +// at this point, no code has been written to properly detect when it is needed, +// so for now it just draws by default. +func (jp *C2Point) blackWidowMark(ra Adder) { + xprod := jp.TNorm.X*jp.LNorm.Y - jp.TNorm.Y*jp.LNorm.X + if xprod > epsilonFixed*epsilonFixed { + tl := jp.P.Sub(jp.TNorm) + ll := jp.P.Sub(jp.LNorm) + ra.Start(jp.P) + ra.Line(tl) + ra.Line(ll) + ra.Line(jp.P) + } else if xprod < -epsilonFixed*epsilonFixed { + th := jp.P.Add(jp.TNorm) + lh := jp.P.Add(jp.LNorm) + ra.Start(jp.P) + ra.Line(lh) + ra.Line(th) + ra.Line(jp.P) + } +} diff --git a/vendor/golang.org/x/image/AUTHORS b/vendor/golang.org/x/image/AUTHORS new file mode 100644 index 0000000000000..15167cd746c56 --- /dev/null +++ b/vendor/golang.org/x/image/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/image/CONTRIBUTORS b/vendor/golang.org/x/image/CONTRIBUTORS new file mode 100644 index 0000000000000..1c4577e968061 --- /dev/null +++ b/vendor/golang.org/x/image/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/image/LICENSE b/vendor/golang.org/x/image/LICENSE new file mode 100644 index 0000000000000..6a66aea5eafe0 --- /dev/null +++ b/vendor/golang.org/x/image/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +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 Inc. 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. diff --git a/vendor/golang.org/x/image/PATENTS b/vendor/golang.org/x/image/PATENTS new file mode 100644 index 0000000000000..733099041f84f --- /dev/null +++ b/vendor/golang.org/x/image/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google 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, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/image/colornames/colornames.go b/vendor/golang.org/x/image/colornames/colornames.go new file mode 100644 index 0000000000000..fa94d426afc27 --- /dev/null +++ b/vendor/golang.org/x/image/colornames/colornames.go @@ -0,0 +1,10 @@ +// Copyright 2015 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 + +// Package colornames provides named colors as defined in the SVG 1.1 spec. +// +// See http://www.w3.org/TR/SVG/types.html#ColorKeywords +package colornames diff --git a/vendor/golang.org/x/image/colornames/table.go b/vendor/golang.org/x/image/colornames/table.go new file mode 100644 index 0000000000000..7b6f1f47a72f0 --- /dev/null +++ b/vendor/golang.org/x/image/colornames/table.go @@ -0,0 +1,457 @@ +// generated by go generate; DO NOT EDIT. + +package colornames + +import "image/color" + +// Map contains named colors defined in the SVG 1.1 spec. +var Map = map[string]color.RGBA{ + "aliceblue": color.RGBA{0xf0, 0xf8, 0xff, 0xff}, // rgb(240, 248, 255) + "antiquewhite": color.RGBA{0xfa, 0xeb, 0xd7, 0xff}, // rgb(250, 235, 215) + "aqua": color.RGBA{0x00, 0xff, 0xff, 0xff}, // rgb(0, 255, 255) + "aquamarine": color.RGBA{0x7f, 0xff, 0xd4, 0xff}, // rgb(127, 255, 212) + "azure": color.RGBA{0xf0, 0xff, 0xff, 0xff}, // rgb(240, 255, 255) + "beige": color.RGBA{0xf5, 0xf5, 0xdc, 0xff}, // rgb(245, 245, 220) + "bisque": color.RGBA{0xff, 0xe4, 0xc4, 0xff}, // rgb(255, 228, 196) + "black": color.RGBA{0x00, 0x00, 0x00, 0xff}, // rgb(0, 0, 0) + "blanchedalmond": color.RGBA{0xff, 0xeb, 0xcd, 0xff}, // rgb(255, 235, 205) + "blue": color.RGBA{0x00, 0x00, 0xff, 0xff}, // rgb(0, 0, 255) + "blueviolet": color.RGBA{0x8a, 0x2b, 0xe2, 0xff}, // rgb(138, 43, 226) + "brown": color.RGBA{0xa5, 0x2a, 0x2a, 0xff}, // rgb(165, 42, 42) + "burlywood": color.RGBA{0xde, 0xb8, 0x87, 0xff}, // rgb(222, 184, 135) + "cadetblue": color.RGBA{0x5f, 0x9e, 0xa0, 0xff}, // rgb(95, 158, 160) + "chartreuse": color.RGBA{0x7f, 0xff, 0x00, 0xff}, // rgb(127, 255, 0) + "chocolate": color.RGBA{0xd2, 0x69, 0x1e, 0xff}, // rgb(210, 105, 30) + "coral": color.RGBA{0xff, 0x7f, 0x50, 0xff}, // rgb(255, 127, 80) + "cornflowerblue": color.RGBA{0x64, 0x95, 0xed, 0xff}, // rgb(100, 149, 237) + "cornsilk": color.RGBA{0xff, 0xf8, 0xdc, 0xff}, // rgb(255, 248, 220) + "crimson": color.RGBA{0xdc, 0x14, 0x3c, 0xff}, // rgb(220, 20, 60) + "cyan": color.RGBA{0x00, 0xff, 0xff, 0xff}, // rgb(0, 255, 255) + "darkblue": color.RGBA{0x00, 0x00, 0x8b, 0xff}, // rgb(0, 0, 139) + "darkcyan": color.RGBA{0x00, 0x8b, 0x8b, 0xff}, // rgb(0, 139, 139) + "darkgoldenrod": color.RGBA{0xb8, 0x86, 0x0b, 0xff}, // rgb(184, 134, 11) + "darkgray": color.RGBA{0xa9, 0xa9, 0xa9, 0xff}, // rgb(169, 169, 169) + "darkgreen": color.RGBA{0x00, 0x64, 0x00, 0xff}, // rgb(0, 100, 0) + "darkgrey": color.RGBA{0xa9, 0xa9, 0xa9, 0xff}, // rgb(169, 169, 169) + "darkkhaki": color.RGBA{0xbd, 0xb7, 0x6b, 0xff}, // rgb(189, 183, 107) + "darkmagenta": color.RGBA{0x8b, 0x00, 0x8b, 0xff}, // rgb(139, 0, 139) + "darkolivegreen": color.RGBA{0x55, 0x6b, 0x2f, 0xff}, // rgb(85, 107, 47) + "darkorange": color.RGBA{0xff, 0x8c, 0x00, 0xff}, // rgb(255, 140, 0) + "darkorchid": color.RGBA{0x99, 0x32, 0xcc, 0xff}, // rgb(153, 50, 204) + "darkred": color.RGBA{0x8b, 0x00, 0x00, 0xff}, // rgb(139, 0, 0) + "darksalmon": color.RGBA{0xe9, 0x96, 0x7a, 0xff}, // rgb(233, 150, 122) + "darkseagreen": color.RGBA{0x8f, 0xbc, 0x8f, 0xff}, // rgb(143, 188, 143) + "darkslateblue": color.RGBA{0x48, 0x3d, 0x8b, 0xff}, // rgb(72, 61, 139) + "darkslategray": color.RGBA{0x2f, 0x4f, 0x4f, 0xff}, // rgb(47, 79, 79) + "darkslategrey": color.RGBA{0x2f, 0x4f, 0x4f, 0xff}, // rgb(47, 79, 79) + "darkturquoise": color.RGBA{0x00, 0xce, 0xd1, 0xff}, // rgb(0, 206, 209) + "darkviolet": color.RGBA{0x94, 0x00, 0xd3, 0xff}, // rgb(148, 0, 211) + "deeppink": color.RGBA{0xff, 0x14, 0x93, 0xff}, // rgb(255, 20, 147) + "deepskyblue": color.RGBA{0x00, 0xbf, 0xff, 0xff}, // rgb(0, 191, 255) + "dimgray": color.RGBA{0x69, 0x69, 0x69, 0xff}, // rgb(105, 105, 105) + "dimgrey": color.RGBA{0x69, 0x69, 0x69, 0xff}, // rgb(105, 105, 105) + "dodgerblue": color.RGBA{0x1e, 0x90, 0xff, 0xff}, // rgb(30, 144, 255) + "firebrick": color.RGBA{0xb2, 0x22, 0x22, 0xff}, // rgb(178, 34, 34) + "floralwhite": color.RGBA{0xff, 0xfa, 0xf0, 0xff}, // rgb(255, 250, 240) + "forestgreen": color.RGBA{0x22, 0x8b, 0x22, 0xff}, // rgb(34, 139, 34) + "fuchsia": color.RGBA{0xff, 0x00, 0xff, 0xff}, // rgb(255, 0, 255) + "gainsboro": color.RGBA{0xdc, 0xdc, 0xdc, 0xff}, // rgb(220, 220, 220) + "ghostwhite": color.RGBA{0xf8, 0xf8, 0xff, 0xff}, // rgb(248, 248, 255) + "gold": color.RGBA{0xff, 0xd7, 0x00, 0xff}, // rgb(255, 215, 0) + "goldenrod": color.RGBA{0xda, 0xa5, 0x20, 0xff}, // rgb(218, 165, 32) + "gray": color.RGBA{0x80, 0x80, 0x80, 0xff}, // rgb(128, 128, 128) + "green": color.RGBA{0x00, 0x80, 0x00, 0xff}, // rgb(0, 128, 0) + "greenyellow": color.RGBA{0xad, 0xff, 0x2f, 0xff}, // rgb(173, 255, 47) + "grey": color.RGBA{0x80, 0x80, 0x80, 0xff}, // rgb(128, 128, 128) + "honeydew": color.RGBA{0xf0, 0xff, 0xf0, 0xff}, // rgb(240, 255, 240) + "hotpink": color.RGBA{0xff, 0x69, 0xb4, 0xff}, // rgb(255, 105, 180) + "indianred": color.RGBA{0xcd, 0x5c, 0x5c, 0xff}, // rgb(205, 92, 92) + "indigo": color.RGBA{0x4b, 0x00, 0x82, 0xff}, // rgb(75, 0, 130) + "ivory": color.RGBA{0xff, 0xff, 0xf0, 0xff}, // rgb(255, 255, 240) + "khaki": color.RGBA{0xf0, 0xe6, 0x8c, 0xff}, // rgb(240, 230, 140) + "lavender": color.RGBA{0xe6, 0xe6, 0xfa, 0xff}, // rgb(230, 230, 250) + "lavenderblush": color.RGBA{0xff, 0xf0, 0xf5, 0xff}, // rgb(255, 240, 245) + "lawngreen": color.RGBA{0x7c, 0xfc, 0x00, 0xff}, // rgb(124, 252, 0) + "lemonchiffon": color.RGBA{0xff, 0xfa, 0xcd, 0xff}, // rgb(255, 250, 205) + "lightblue": color.RGBA{0xad, 0xd8, 0xe6, 0xff}, // rgb(173, 216, 230) + "lightcoral": color.RGBA{0xf0, 0x80, 0x80, 0xff}, // rgb(240, 128, 128) + "lightcyan": color.RGBA{0xe0, 0xff, 0xff, 0xff}, // rgb(224, 255, 255) + "lightgoldenrodyellow": color.RGBA{0xfa, 0xfa, 0xd2, 0xff}, // rgb(250, 250, 210) + "lightgray": color.RGBA{0xd3, 0xd3, 0xd3, 0xff}, // rgb(211, 211, 211) + "lightgreen": color.RGBA{0x90, 0xee, 0x90, 0xff}, // rgb(144, 238, 144) + "lightgrey": color.RGBA{0xd3, 0xd3, 0xd3, 0xff}, // rgb(211, 211, 211) + "lightpink": color.RGBA{0xff, 0xb6, 0xc1, 0xff}, // rgb(255, 182, 193) + "lightsalmon": color.RGBA{0xff, 0xa0, 0x7a, 0xff}, // rgb(255, 160, 122) + "lightseagreen": color.RGBA{0x20, 0xb2, 0xaa, 0xff}, // rgb(32, 178, 170) + "lightskyblue": color.RGBA{0x87, 0xce, 0xfa, 0xff}, // rgb(135, 206, 250) + "lightslategray": color.RGBA{0x77, 0x88, 0x99, 0xff}, // rgb(119, 136, 153) + "lightslategrey": color.RGBA{0x77, 0x88, 0x99, 0xff}, // rgb(119, 136, 153) + "lightsteelblue": color.RGBA{0xb0, 0xc4, 0xde, 0xff}, // rgb(176, 196, 222) + "lightyellow": color.RGBA{0xff, 0xff, 0xe0, 0xff}, // rgb(255, 255, 224) + "lime": color.RGBA{0x00, 0xff, 0x00, 0xff}, // rgb(0, 255, 0) + "limegreen": color.RGBA{0x32, 0xcd, 0x32, 0xff}, // rgb(50, 205, 50) + "linen": color.RGBA{0xfa, 0xf0, 0xe6, 0xff}, // rgb(250, 240, 230) + "magenta": color.RGBA{0xff, 0x00, 0xff, 0xff}, // rgb(255, 0, 255) + "maroon": color.RGBA{0x80, 0x00, 0x00, 0xff}, // rgb(128, 0, 0) + "mediumaquamarine": color.RGBA{0x66, 0xcd, 0xaa, 0xff}, // rgb(102, 205, 170) + "mediumblue": color.RGBA{0x00, 0x00, 0xcd, 0xff}, // rgb(0, 0, 205) + "mediumorchid": color.RGBA{0xba, 0x55, 0xd3, 0xff}, // rgb(186, 85, 211) + "mediumpurple": color.RGBA{0x93, 0x70, 0xdb, 0xff}, // rgb(147, 112, 219) + "mediumseagreen": color.RGBA{0x3c, 0xb3, 0x71, 0xff}, // rgb(60, 179, 113) + "mediumslateblue": color.RGBA{0x7b, 0x68, 0xee, 0xff}, // rgb(123, 104, 238) + "mediumspringgreen": color.RGBA{0x00, 0xfa, 0x9a, 0xff}, // rgb(0, 250, 154) + "mediumturquoise": color.RGBA{0x48, 0xd1, 0xcc, 0xff}, // rgb(72, 209, 204) + "mediumvioletred": color.RGBA{0xc7, 0x15, 0x85, 0xff}, // rgb(199, 21, 133) + "midnightblue": color.RGBA{0x19, 0x19, 0x70, 0xff}, // rgb(25, 25, 112) + "mintcream": color.RGBA{0xf5, 0xff, 0xfa, 0xff}, // rgb(245, 255, 250) + "mistyrose": color.RGBA{0xff, 0xe4, 0xe1, 0xff}, // rgb(255, 228, 225) + "moccasin": color.RGBA{0xff, 0xe4, 0xb5, 0xff}, // rgb(255, 228, 181) + "navajowhite": color.RGBA{0xff, 0xde, 0xad, 0xff}, // rgb(255, 222, 173) + "navy": color.RGBA{0x00, 0x00, 0x80, 0xff}, // rgb(0, 0, 128) + "oldlace": color.RGBA{0xfd, 0xf5, 0xe6, 0xff}, // rgb(253, 245, 230) + "olive": color.RGBA{0x80, 0x80, 0x00, 0xff}, // rgb(128, 128, 0) + "olivedrab": color.RGBA{0x6b, 0x8e, 0x23, 0xff}, // rgb(107, 142, 35) + "orange": color.RGBA{0xff, 0xa5, 0x00, 0xff}, // rgb(255, 165, 0) + "orangered": color.RGBA{0xff, 0x45, 0x00, 0xff}, // rgb(255, 69, 0) + "orchid": color.RGBA{0xda, 0x70, 0xd6, 0xff}, // rgb(218, 112, 214) + "palegoldenrod": color.RGBA{0xee, 0xe8, 0xaa, 0xff}, // rgb(238, 232, 170) + "palegreen": color.RGBA{0x98, 0xfb, 0x98, 0xff}, // rgb(152, 251, 152) + "paleturquoise": color.RGBA{0xaf, 0xee, 0xee, 0xff}, // rgb(175, 238, 238) + "palevioletred": color.RGBA{0xdb, 0x70, 0x93, 0xff}, // rgb(219, 112, 147) + "papayawhip": color.RGBA{0xff, 0xef, 0xd5, 0xff}, // rgb(255, 239, 213) + "peachpuff": color.RGBA{0xff, 0xda, 0xb9, 0xff}, // rgb(255, 218, 185) + "peru": color.RGBA{0xcd, 0x85, 0x3f, 0xff}, // rgb(205, 133, 63) + "pink": color.RGBA{0xff, 0xc0, 0xcb, 0xff}, // rgb(255, 192, 203) + "plum": color.RGBA{0xdd, 0xa0, 0xdd, 0xff}, // rgb(221, 160, 221) + "powderblue": color.RGBA{0xb0, 0xe0, 0xe6, 0xff}, // rgb(176, 224, 230) + "purple": color.RGBA{0x80, 0x00, 0x80, 0xff}, // rgb(128, 0, 128) + "red": color.RGBA{0xff, 0x00, 0x00, 0xff}, // rgb(255, 0, 0) + "rosybrown": color.RGBA{0xbc, 0x8f, 0x8f, 0xff}, // rgb(188, 143, 143) + "royalblue": color.RGBA{0x41, 0x69, 0xe1, 0xff}, // rgb(65, 105, 225) + "saddlebrown": color.RGBA{0x8b, 0x45, 0x13, 0xff}, // rgb(139, 69, 19) + "salmon": color.RGBA{0xfa, 0x80, 0x72, 0xff}, // rgb(250, 128, 114) + "sandybrown": color.RGBA{0xf4, 0xa4, 0x60, 0xff}, // rgb(244, 164, 96) + "seagreen": color.RGBA{0x2e, 0x8b, 0x57, 0xff}, // rgb(46, 139, 87) + "seashell": color.RGBA{0xff, 0xf5, 0xee, 0xff}, // rgb(255, 245, 238) + "sienna": color.RGBA{0xa0, 0x52, 0x2d, 0xff}, // rgb(160, 82, 45) + "silver": color.RGBA{0xc0, 0xc0, 0xc0, 0xff}, // rgb(192, 192, 192) + "skyblue": color.RGBA{0x87, 0xce, 0xeb, 0xff}, // rgb(135, 206, 235) + "slateblue": color.RGBA{0x6a, 0x5a, 0xcd, 0xff}, // rgb(106, 90, 205) + "slategray": color.RGBA{0x70, 0x80, 0x90, 0xff}, // rgb(112, 128, 144) + "slategrey": color.RGBA{0x70, 0x80, 0x90, 0xff}, // rgb(112, 128, 144) + "snow": color.RGBA{0xff, 0xfa, 0xfa, 0xff}, // rgb(255, 250, 250) + "springgreen": color.RGBA{0x00, 0xff, 0x7f, 0xff}, // rgb(0, 255, 127) + "steelblue": color.RGBA{0x46, 0x82, 0xb4, 0xff}, // rgb(70, 130, 180) + "tan": color.RGBA{0xd2, 0xb4, 0x8c, 0xff}, // rgb(210, 180, 140) + "teal": color.RGBA{0x00, 0x80, 0x80, 0xff}, // rgb(0, 128, 128) + "thistle": color.RGBA{0xd8, 0xbf, 0xd8, 0xff}, // rgb(216, 191, 216) + "tomato": color.RGBA{0xff, 0x63, 0x47, 0xff}, // rgb(255, 99, 71) + "turquoise": color.RGBA{0x40, 0xe0, 0xd0, 0xff}, // rgb(64, 224, 208) + "violet": color.RGBA{0xee, 0x82, 0xee, 0xff}, // rgb(238, 130, 238) + "wheat": color.RGBA{0xf5, 0xde, 0xb3, 0xff}, // rgb(245, 222, 179) + "white": color.RGBA{0xff, 0xff, 0xff, 0xff}, // rgb(255, 255, 255) + "whitesmoke": color.RGBA{0xf5, 0xf5, 0xf5, 0xff}, // rgb(245, 245, 245) + "yellow": color.RGBA{0xff, 0xff, 0x00, 0xff}, // rgb(255, 255, 0) + "yellowgreen": color.RGBA{0x9a, 0xcd, 0x32, 0xff}, // rgb(154, 205, 50) +} + +// Names contains the color names defined in the SVG 1.1 spec. +var Names = []string{ + "aliceblue", + "antiquewhite", + "aqua", + "aquamarine", + "azure", + "beige", + "bisque", + "black", + "blanchedalmond", + "blue", + "blueviolet", + "brown", + "burlywood", + "cadetblue", + "chartreuse", + "chocolate", + "coral", + "cornflowerblue", + "cornsilk", + "crimson", + "cyan", + "darkblue", + "darkcyan", + "darkgoldenrod", + "darkgray", + "darkgreen", + "darkgrey", + "darkkhaki", + "darkmagenta", + "darkolivegreen", + "darkorange", + "darkorchid", + "darkred", + "darksalmon", + "darkseagreen", + "darkslateblue", + "darkslategray", + "darkslategrey", + "darkturquoise", + "darkviolet", + "deeppink", + "deepskyblue", + "dimgray", + "dimgrey", + "dodgerblue", + "firebrick", + "floralwhite", + "forestgreen", + "fuchsia", + "gainsboro", + "ghostwhite", + "gold", + "goldenrod", + "gray", + "green", + "greenyellow", + "grey", + "honeydew", + "hotpink", + "indianred", + "indigo", + "ivory", + "khaki", + "lavender", + "lavenderblush", + "lawngreen", + "lemonchiffon", + "lightblue", + "lightcoral", + "lightcyan", + "lightgoldenrodyellow", + "lightgray", + "lightgreen", + "lightgrey", + "lightpink", + "lightsalmon", + "lightseagreen", + "lightskyblue", + "lightslategray", + "lightslategrey", + "lightsteelblue", + "lightyellow", + "lime", + "limegreen", + "linen", + "magenta", + "maroon", + "mediumaquamarine", + "mediumblue", + "mediumorchid", + "mediumpurple", + "mediumseagreen", + "mediumslateblue", + "mediumspringgreen", + "mediumturquoise", + "mediumvioletred", + "midnightblue", + "mintcream", + "mistyrose", + "moccasin", + "navajowhite", + "navy", + "oldlace", + "olive", + "olivedrab", + "orange", + "orangered", + "orchid", + "palegoldenrod", + "palegreen", + "paleturquoise", + "palevioletred", + "papayawhip", + "peachpuff", + "peru", + "pink", + "plum", + "powderblue", + "purple", + "red", + "rosybrown", + "royalblue", + "saddlebrown", + "salmon", + "sandybrown", + "seagreen", + "seashell", + "sienna", + "silver", + "skyblue", + "slateblue", + "slategray", + "slategrey", + "snow", + "springgreen", + "steelblue", + "tan", + "teal", + "thistle", + "tomato", + "turquoise", + "violet", + "wheat", + "white", + "whitesmoke", + "yellow", + "yellowgreen", +} + +var ( + Aliceblue = color.RGBA{0xf0, 0xf8, 0xff, 0xff} // rgb(240, 248, 255) + Antiquewhite = color.RGBA{0xfa, 0xeb, 0xd7, 0xff} // rgb(250, 235, 215) + Aqua = color.RGBA{0x00, 0xff, 0xff, 0xff} // rgb(0, 255, 255) + Aquamarine = color.RGBA{0x7f, 0xff, 0xd4, 0xff} // rgb(127, 255, 212) + Azure = color.RGBA{0xf0, 0xff, 0xff, 0xff} // rgb(240, 255, 255) + Beige = color.RGBA{0xf5, 0xf5, 0xdc, 0xff} // rgb(245, 245, 220) + Bisque = color.RGBA{0xff, 0xe4, 0xc4, 0xff} // rgb(255, 228, 196) + Black = color.RGBA{0x00, 0x00, 0x00, 0xff} // rgb(0, 0, 0) + Blanchedalmond = color.RGBA{0xff, 0xeb, 0xcd, 0xff} // rgb(255, 235, 205) + Blue = color.RGBA{0x00, 0x00, 0xff, 0xff} // rgb(0, 0, 255) + Blueviolet = color.RGBA{0x8a, 0x2b, 0xe2, 0xff} // rgb(138, 43, 226) + Brown = color.RGBA{0xa5, 0x2a, 0x2a, 0xff} // rgb(165, 42, 42) + Burlywood = color.RGBA{0xde, 0xb8, 0x87, 0xff} // rgb(222, 184, 135) + Cadetblue = color.RGBA{0x5f, 0x9e, 0xa0, 0xff} // rgb(95, 158, 160) + Chartreuse = color.RGBA{0x7f, 0xff, 0x00, 0xff} // rgb(127, 255, 0) + Chocolate = color.RGBA{0xd2, 0x69, 0x1e, 0xff} // rgb(210, 105, 30) + Coral = color.RGBA{0xff, 0x7f, 0x50, 0xff} // rgb(255, 127, 80) + Cornflowerblue = color.RGBA{0x64, 0x95, 0xed, 0xff} // rgb(100, 149, 237) + Cornsilk = color.RGBA{0xff, 0xf8, 0xdc, 0xff} // rgb(255, 248, 220) + Crimson = color.RGBA{0xdc, 0x14, 0x3c, 0xff} // rgb(220, 20, 60) + Cyan = color.RGBA{0x00, 0xff, 0xff, 0xff} // rgb(0, 255, 255) + Darkblue = color.RGBA{0x00, 0x00, 0x8b, 0xff} // rgb(0, 0, 139) + Darkcyan = color.RGBA{0x00, 0x8b, 0x8b, 0xff} // rgb(0, 139, 139) + Darkgoldenrod = color.RGBA{0xb8, 0x86, 0x0b, 0xff} // rgb(184, 134, 11) + Darkgray = color.RGBA{0xa9, 0xa9, 0xa9, 0xff} // rgb(169, 169, 169) + Darkgreen = color.RGBA{0x00, 0x64, 0x00, 0xff} // rgb(0, 100, 0) + Darkgrey = color.RGBA{0xa9, 0xa9, 0xa9, 0xff} // rgb(169, 169, 169) + Darkkhaki = color.RGBA{0xbd, 0xb7, 0x6b, 0xff} // rgb(189, 183, 107) + Darkmagenta = color.RGBA{0x8b, 0x00, 0x8b, 0xff} // rgb(139, 0, 139) + Darkolivegreen = color.RGBA{0x55, 0x6b, 0x2f, 0xff} // rgb(85, 107, 47) + Darkorange = color.RGBA{0xff, 0x8c, 0x00, 0xff} // rgb(255, 140, 0) + Darkorchid = color.RGBA{0x99, 0x32, 0xcc, 0xff} // rgb(153, 50, 204) + Darkred = color.RGBA{0x8b, 0x00, 0x00, 0xff} // rgb(139, 0, 0) + Darksalmon = color.RGBA{0xe9, 0x96, 0x7a, 0xff} // rgb(233, 150, 122) + Darkseagreen = color.RGBA{0x8f, 0xbc, 0x8f, 0xff} // rgb(143, 188, 143) + Darkslateblue = color.RGBA{0x48, 0x3d, 0x8b, 0xff} // rgb(72, 61, 139) + Darkslategray = color.RGBA{0x2f, 0x4f, 0x4f, 0xff} // rgb(47, 79, 79) + Darkslategrey = color.RGBA{0x2f, 0x4f, 0x4f, 0xff} // rgb(47, 79, 79) + Darkturquoise = color.RGBA{0x00, 0xce, 0xd1, 0xff} // rgb(0, 206, 209) + Darkviolet = color.RGBA{0x94, 0x00, 0xd3, 0xff} // rgb(148, 0, 211) + Deeppink = color.RGBA{0xff, 0x14, 0x93, 0xff} // rgb(255, 20, 147) + Deepskyblue = color.RGBA{0x00, 0xbf, 0xff, 0xff} // rgb(0, 191, 255) + Dimgray = color.RGBA{0x69, 0x69, 0x69, 0xff} // rgb(105, 105, 105) + Dimgrey = color.RGBA{0x69, 0x69, 0x69, 0xff} // rgb(105, 105, 105) + Dodgerblue = color.RGBA{0x1e, 0x90, 0xff, 0xff} // rgb(30, 144, 255) + Firebrick = color.RGBA{0xb2, 0x22, 0x22, 0xff} // rgb(178, 34, 34) + Floralwhite = color.RGBA{0xff, 0xfa, 0xf0, 0xff} // rgb(255, 250, 240) + Forestgreen = color.RGBA{0x22, 0x8b, 0x22, 0xff} // rgb(34, 139, 34) + Fuchsia = color.RGBA{0xff, 0x00, 0xff, 0xff} // rgb(255, 0, 255) + Gainsboro = color.RGBA{0xdc, 0xdc, 0xdc, 0xff} // rgb(220, 220, 220) + Ghostwhite = color.RGBA{0xf8, 0xf8, 0xff, 0xff} // rgb(248, 248, 255) + Gold = color.RGBA{0xff, 0xd7, 0x00, 0xff} // rgb(255, 215, 0) + Goldenrod = color.RGBA{0xda, 0xa5, 0x20, 0xff} // rgb(218, 165, 32) + Gray = color.RGBA{0x80, 0x80, 0x80, 0xff} // rgb(128, 128, 128) + Green = color.RGBA{0x00, 0x80, 0x00, 0xff} // rgb(0, 128, 0) + Greenyellow = color.RGBA{0xad, 0xff, 0x2f, 0xff} // rgb(173, 255, 47) + Grey = color.RGBA{0x80, 0x80, 0x80, 0xff} // rgb(128, 128, 128) + Honeydew = color.RGBA{0xf0, 0xff, 0xf0, 0xff} // rgb(240, 255, 240) + Hotpink = color.RGBA{0xff, 0x69, 0xb4, 0xff} // rgb(255, 105, 180) + Indianred = color.RGBA{0xcd, 0x5c, 0x5c, 0xff} // rgb(205, 92, 92) + Indigo = color.RGBA{0x4b, 0x00, 0x82, 0xff} // rgb(75, 0, 130) + Ivory = color.RGBA{0xff, 0xff, 0xf0, 0xff} // rgb(255, 255, 240) + Khaki = color.RGBA{0xf0, 0xe6, 0x8c, 0xff} // rgb(240, 230, 140) + Lavender = color.RGBA{0xe6, 0xe6, 0xfa, 0xff} // rgb(230, 230, 250) + Lavenderblush = color.RGBA{0xff, 0xf0, 0xf5, 0xff} // rgb(255, 240, 245) + Lawngreen = color.RGBA{0x7c, 0xfc, 0x00, 0xff} // rgb(124, 252, 0) + Lemonchiffon = color.RGBA{0xff, 0xfa, 0xcd, 0xff} // rgb(255, 250, 205) + Lightblue = color.RGBA{0xad, 0xd8, 0xe6, 0xff} // rgb(173, 216, 230) + Lightcoral = color.RGBA{0xf0, 0x80, 0x80, 0xff} // rgb(240, 128, 128) + Lightcyan = color.RGBA{0xe0, 0xff, 0xff, 0xff} // rgb(224, 255, 255) + Lightgoldenrodyellow = color.RGBA{0xfa, 0xfa, 0xd2, 0xff} // rgb(250, 250, 210) + Lightgray = color.RGBA{0xd3, 0xd3, 0xd3, 0xff} // rgb(211, 211, 211) + Lightgreen = color.RGBA{0x90, 0xee, 0x90, 0xff} // rgb(144, 238, 144) + Lightgrey = color.RGBA{0xd3, 0xd3, 0xd3, 0xff} // rgb(211, 211, 211) + Lightpink = color.RGBA{0xff, 0xb6, 0xc1, 0xff} // rgb(255, 182, 193) + Lightsalmon = color.RGBA{0xff, 0xa0, 0x7a, 0xff} // rgb(255, 160, 122) + Lightseagreen = color.RGBA{0x20, 0xb2, 0xaa, 0xff} // rgb(32, 178, 170) + Lightskyblue = color.RGBA{0x87, 0xce, 0xfa, 0xff} // rgb(135, 206, 250) + Lightslategray = color.RGBA{0x77, 0x88, 0x99, 0xff} // rgb(119, 136, 153) + Lightslategrey = color.RGBA{0x77, 0x88, 0x99, 0xff} // rgb(119, 136, 153) + Lightsteelblue = color.RGBA{0xb0, 0xc4, 0xde, 0xff} // rgb(176, 196, 222) + Lightyellow = color.RGBA{0xff, 0xff, 0xe0, 0xff} // rgb(255, 255, 224) + Lime = color.RGBA{0x00, 0xff, 0x00, 0xff} // rgb(0, 255, 0) + Limegreen = color.RGBA{0x32, 0xcd, 0x32, 0xff} // rgb(50, 205, 50) + Linen = color.RGBA{0xfa, 0xf0, 0xe6, 0xff} // rgb(250, 240, 230) + Magenta = color.RGBA{0xff, 0x00, 0xff, 0xff} // rgb(255, 0, 255) + Maroon = color.RGBA{0x80, 0x00, 0x00, 0xff} // rgb(128, 0, 0) + Mediumaquamarine = color.RGBA{0x66, 0xcd, 0xaa, 0xff} // rgb(102, 205, 170) + Mediumblue = color.RGBA{0x00, 0x00, 0xcd, 0xff} // rgb(0, 0, 205) + Mediumorchid = color.RGBA{0xba, 0x55, 0xd3, 0xff} // rgb(186, 85, 211) + Mediumpurple = color.RGBA{0x93, 0x70, 0xdb, 0xff} // rgb(147, 112, 219) + Mediumseagreen = color.RGBA{0x3c, 0xb3, 0x71, 0xff} // rgb(60, 179, 113) + Mediumslateblue = color.RGBA{0x7b, 0x68, 0xee, 0xff} // rgb(123, 104, 238) + Mediumspringgreen = color.RGBA{0x00, 0xfa, 0x9a, 0xff} // rgb(0, 250, 154) + Mediumturquoise = color.RGBA{0x48, 0xd1, 0xcc, 0xff} // rgb(72, 209, 204) + Mediumvioletred = color.RGBA{0xc7, 0x15, 0x85, 0xff} // rgb(199, 21, 133) + Midnightblue = color.RGBA{0x19, 0x19, 0x70, 0xff} // rgb(25, 25, 112) + Mintcream = color.RGBA{0xf5, 0xff, 0xfa, 0xff} // rgb(245, 255, 250) + Mistyrose = color.RGBA{0xff, 0xe4, 0xe1, 0xff} // rgb(255, 228, 225) + Moccasin = color.RGBA{0xff, 0xe4, 0xb5, 0xff} // rgb(255, 228, 181) + Navajowhite = color.RGBA{0xff, 0xde, 0xad, 0xff} // rgb(255, 222, 173) + Navy = color.RGBA{0x00, 0x00, 0x80, 0xff} // rgb(0, 0, 128) + Oldlace = color.RGBA{0xfd, 0xf5, 0xe6, 0xff} // rgb(253, 245, 230) + Olive = color.RGBA{0x80, 0x80, 0x00, 0xff} // rgb(128, 128, 0) + Olivedrab = color.RGBA{0x6b, 0x8e, 0x23, 0xff} // rgb(107, 142, 35) + Orange = color.RGBA{0xff, 0xa5, 0x00, 0xff} // rgb(255, 165, 0) + Orangered = color.RGBA{0xff, 0x45, 0x00, 0xff} // rgb(255, 69, 0) + Orchid = color.RGBA{0xda, 0x70, 0xd6, 0xff} // rgb(218, 112, 214) + Palegoldenrod = color.RGBA{0xee, 0xe8, 0xaa, 0xff} // rgb(238, 232, 170) + Palegreen = color.RGBA{0x98, 0xfb, 0x98, 0xff} // rgb(152, 251, 152) + Paleturquoise = color.RGBA{0xaf, 0xee, 0xee, 0xff} // rgb(175, 238, 238) + Palevioletred = color.RGBA{0xdb, 0x70, 0x93, 0xff} // rgb(219, 112, 147) + Papayawhip = color.RGBA{0xff, 0xef, 0xd5, 0xff} // rgb(255, 239, 213) + Peachpuff = color.RGBA{0xff, 0xda, 0xb9, 0xff} // rgb(255, 218, 185) + Peru = color.RGBA{0xcd, 0x85, 0x3f, 0xff} // rgb(205, 133, 63) + Pink = color.RGBA{0xff, 0xc0, 0xcb, 0xff} // rgb(255, 192, 203) + Plum = color.RGBA{0xdd, 0xa0, 0xdd, 0xff} // rgb(221, 160, 221) + Powderblue = color.RGBA{0xb0, 0xe0, 0xe6, 0xff} // rgb(176, 224, 230) + Purple = color.RGBA{0x80, 0x00, 0x80, 0xff} // rgb(128, 0, 128) + Red = color.RGBA{0xff, 0x00, 0x00, 0xff} // rgb(255, 0, 0) + Rosybrown = color.RGBA{0xbc, 0x8f, 0x8f, 0xff} // rgb(188, 143, 143) + Royalblue = color.RGBA{0x41, 0x69, 0xe1, 0xff} // rgb(65, 105, 225) + Saddlebrown = color.RGBA{0x8b, 0x45, 0x13, 0xff} // rgb(139, 69, 19) + Salmon = color.RGBA{0xfa, 0x80, 0x72, 0xff} // rgb(250, 128, 114) + Sandybrown = color.RGBA{0xf4, 0xa4, 0x60, 0xff} // rgb(244, 164, 96) + Seagreen = color.RGBA{0x2e, 0x8b, 0x57, 0xff} // rgb(46, 139, 87) + Seashell = color.RGBA{0xff, 0xf5, 0xee, 0xff} // rgb(255, 245, 238) + Sienna = color.RGBA{0xa0, 0x52, 0x2d, 0xff} // rgb(160, 82, 45) + Silver = color.RGBA{0xc0, 0xc0, 0xc0, 0xff} // rgb(192, 192, 192) + Skyblue = color.RGBA{0x87, 0xce, 0xeb, 0xff} // rgb(135, 206, 235) + Slateblue = color.RGBA{0x6a, 0x5a, 0xcd, 0xff} // rgb(106, 90, 205) + Slategray = color.RGBA{0x70, 0x80, 0x90, 0xff} // rgb(112, 128, 144) + Slategrey = color.RGBA{0x70, 0x80, 0x90, 0xff} // rgb(112, 128, 144) + Snow = color.RGBA{0xff, 0xfa, 0xfa, 0xff} // rgb(255, 250, 250) + Springgreen = color.RGBA{0x00, 0xff, 0x7f, 0xff} // rgb(0, 255, 127) + Steelblue = color.RGBA{0x46, 0x82, 0xb4, 0xff} // rgb(70, 130, 180) + Tan = color.RGBA{0xd2, 0xb4, 0x8c, 0xff} // rgb(210, 180, 140) + Teal = color.RGBA{0x00, 0x80, 0x80, 0xff} // rgb(0, 128, 128) + Thistle = color.RGBA{0xd8, 0xbf, 0xd8, 0xff} // rgb(216, 191, 216) + Tomato = color.RGBA{0xff, 0x63, 0x47, 0xff} // rgb(255, 99, 71) + Turquoise = color.RGBA{0x40, 0xe0, 0xd0, 0xff} // rgb(64, 224, 208) + Violet = color.RGBA{0xee, 0x82, 0xee, 0xff} // rgb(238, 130, 238) + Wheat = color.RGBA{0xf5, 0xde, 0xb3, 0xff} // rgb(245, 222, 179) + White = color.RGBA{0xff, 0xff, 0xff, 0xff} // rgb(255, 255, 255) + Whitesmoke = color.RGBA{0xf5, 0xf5, 0xf5, 0xff} // rgb(245, 245, 245) + Yellow = color.RGBA{0xff, 0xff, 0x00, 0xff} // rgb(255, 255, 0) + Yellowgreen = color.RGBA{0x9a, 0xcd, 0x32, 0xff} // rgb(154, 205, 50) +) diff --git a/vendor/golang.org/x/image/math/fixed/fixed.go b/vendor/golang.org/x/image/math/fixed/fixed.go new file mode 100644 index 0000000000000..3d916638f1fcf --- /dev/null +++ b/vendor/golang.org/x/image/math/fixed/fixed.go @@ -0,0 +1,410 @@ +// Copyright 2015 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 fixed implements fixed-point integer types. +package fixed // import "golang.org/x/image/math/fixed" + +import ( + "fmt" +) + +// TODO: implement fmt.Formatter for %f and %g. + +// I returns the integer value i as an Int26_6. +// +// For example, passing the integer value 2 yields Int26_6(128). +func I(i int) Int26_6 { + return Int26_6(i << 6) +} + +// Int26_6 is a signed 26.6 fixed-point number. +// +// The integer part ranges from -33554432 to 33554431, inclusive. The +// fractional part has 6 bits of precision. +// +// For example, the number one-and-a-quarter is Int26_6(1<<6 + 1<<4). +type Int26_6 int32 + +// String returns a human-readable representation of a 26.6 fixed-point number. +// +// For example, the number one-and-a-quarter becomes "1:16". +func (x Int26_6) String() string { + const shift, mask = 6, 1<<6 - 1 + if x >= 0 { + return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask)) + } + x = -x + if x >= 0 { + return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask)) + } + return "-33554432:00" // The minimum value is -(1<<25). +} + +// Floor returns the greatest integer value less than or equal to x. +// +// Its return type is int, not Int26_6. +func (x Int26_6) Floor() int { return int((x + 0x00) >> 6) } + +// Round returns the nearest integer value to x. Ties are rounded up. +// +// Its return type is int, not Int26_6. +func (x Int26_6) Round() int { return int((x + 0x20) >> 6) } + +// Ceil returns the least integer value greater than or equal to x. +// +// Its return type is int, not Int26_6. +func (x Int26_6) Ceil() int { return int((x + 0x3f) >> 6) } + +// Mul returns x*y in 26.6 fixed-point arithmetic. +func (x Int26_6) Mul(y Int26_6) Int26_6 { + return Int26_6((int64(x)*int64(y) + 1<<5) >> 6) +} + +// Int52_12 is a signed 52.12 fixed-point number. +// +// The integer part ranges from -2251799813685248 to 2251799813685247, +// inclusive. The fractional part has 12 bits of precision. +// +// For example, the number one-and-a-quarter is Int52_12(1<<12 + 1<<10). +type Int52_12 int64 + +// String returns a human-readable representation of a 52.12 fixed-point +// number. +// +// For example, the number one-and-a-quarter becomes "1:1024". +func (x Int52_12) String() string { + const shift, mask = 12, 1<<12 - 1 + if x >= 0 { + return fmt.Sprintf("%d:%04d", int64(x>>shift), int64(x&mask)) + } + x = -x + if x >= 0 { + return fmt.Sprintf("-%d:%04d", int64(x>>shift), int64(x&mask)) + } + return "-2251799813685248:0000" // The minimum value is -(1<<51). +} + +// Floor returns the greatest integer value less than or equal to x. +// +// Its return type is int, not Int52_12. +func (x Int52_12) Floor() int { return int((x + 0x000) >> 12) } + +// Round returns the nearest integer value to x. Ties are rounded up. +// +// Its return type is int, not Int52_12. +func (x Int52_12) Round() int { return int((x + 0x800) >> 12) } + +// Ceil returns the least integer value greater than or equal to x. +// +// Its return type is int, not Int52_12. +func (x Int52_12) Ceil() int { return int((x + 0xfff) >> 12) } + +// Mul returns x*y in 52.12 fixed-point arithmetic. +func (x Int52_12) Mul(y Int52_12) Int52_12 { + const M, N = 52, 12 + lo, hi := muli64(int64(x), int64(y)) + ret := Int52_12(hi<>N) + ret += Int52_12((lo >> (N - 1)) & 1) // Round to nearest, instead of rounding down. + return ret +} + +// muli64 multiplies two int64 values, returning the 128-bit signed integer +// result as two uint64 values. +// +// This implementation is similar to $GOROOT/src/runtime/softfloat64.go's mullu +// function, which is in turn adapted from Hacker's Delight. +func muli64(u, v int64) (lo, hi uint64) { + const ( + s = 32 + mask = 1<> s) + u0 := uint64(u & mask) + v1 := uint64(v >> s) + v0 := uint64(v & mask) + + w0 := u0 * v0 + t := u1*v0 + w0>>s + w1 := t & mask + w2 := uint64(int64(t) >> s) + w1 += u0 * v1 + return uint64(u) * uint64(v), u1*v1 + w2 + uint64(int64(w1)>>s) +} + +// P returns the integer values x and y as a Point26_6. +// +// For example, passing the integer values (2, -3) yields Point26_6{128, -192}. +func P(x, y int) Point26_6 { + return Point26_6{Int26_6(x << 6), Int26_6(y << 6)} +} + +// Point26_6 is a 26.6 fixed-point coordinate pair. +// +// It is analogous to the image.Point type in the standard library. +type Point26_6 struct { + X, Y Int26_6 +} + +// Add returns the vector p+q. +func (p Point26_6) Add(q Point26_6) Point26_6 { + return Point26_6{p.X + q.X, p.Y + q.Y} +} + +// Sub returns the vector p-q. +func (p Point26_6) Sub(q Point26_6) Point26_6 { + return Point26_6{p.X - q.X, p.Y - q.Y} +} + +// Mul returns the vector p*k. +func (p Point26_6) Mul(k Int26_6) Point26_6 { + return Point26_6{p.X * k / 64, p.Y * k / 64} +} + +// Div returns the vector p/k. +func (p Point26_6) Div(k Int26_6) Point26_6 { + return Point26_6{p.X * 64 / k, p.Y * 64 / k} +} + +// In returns whether p is in r. +func (p Point26_6) In(r Rectangle26_6) bool { + return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y +} + +// Point52_12 is a 52.12 fixed-point coordinate pair. +// +// It is analogous to the image.Point type in the standard library. +type Point52_12 struct { + X, Y Int52_12 +} + +// Add returns the vector p+q. +func (p Point52_12) Add(q Point52_12) Point52_12 { + return Point52_12{p.X + q.X, p.Y + q.Y} +} + +// Sub returns the vector p-q. +func (p Point52_12) Sub(q Point52_12) Point52_12 { + return Point52_12{p.X - q.X, p.Y - q.Y} +} + +// Mul returns the vector p*k. +func (p Point52_12) Mul(k Int52_12) Point52_12 { + return Point52_12{p.X * k / 4096, p.Y * k / 4096} +} + +// Div returns the vector p/k. +func (p Point52_12) Div(k Int52_12) Point52_12 { + return Point52_12{p.X * 4096 / k, p.Y * 4096 / k} +} + +// In returns whether p is in r. +func (p Point52_12) In(r Rectangle52_12) bool { + return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y +} + +// R returns the integer values minX, minY, maxX, maxY as a Rectangle26_6. +// +// For example, passing the integer values (0, 1, 2, 3) yields +// Rectangle26_6{Point26_6{0, 64}, Point26_6{128, 192}}. +// +// Like the image.Rect function in the standard library, the returned rectangle +// has minimum and maximum coordinates swapped if necessary so that it is +// well-formed. +func R(minX, minY, maxX, maxY int) Rectangle26_6 { + if minX > maxX { + minX, maxX = maxX, minX + } + if minY > maxY { + minY, maxY = maxY, minY + } + return Rectangle26_6{ + Point26_6{ + Int26_6(minX << 6), + Int26_6(minY << 6), + }, + Point26_6{ + Int26_6(maxX << 6), + Int26_6(maxY << 6), + }, + } +} + +// Rectangle26_6 is a 26.6 fixed-point coordinate rectangle. The Min bound is +// inclusive and the Max bound is exclusive. It is well-formed if Min.X <= +// Max.X and likewise for Y. +// +// It is analogous to the image.Rectangle type in the standard library. +type Rectangle26_6 struct { + Min, Max Point26_6 +} + +// Add returns the rectangle r translated by p. +func (r Rectangle26_6) Add(p Point26_6) Rectangle26_6 { + return Rectangle26_6{ + Point26_6{r.Min.X + p.X, r.Min.Y + p.Y}, + Point26_6{r.Max.X + p.X, r.Max.Y + p.Y}, + } +} + +// Sub returns the rectangle r translated by -p. +func (r Rectangle26_6) Sub(p Point26_6) Rectangle26_6 { + return Rectangle26_6{ + Point26_6{r.Min.X - p.X, r.Min.Y - p.Y}, + Point26_6{r.Max.X - p.X, r.Max.Y - p.Y}, + } +} + +// Intersect returns the largest rectangle contained by both r and s. If the +// two rectangles do not overlap then the zero rectangle will be returned. +func (r Rectangle26_6) Intersect(s Rectangle26_6) Rectangle26_6 { + if r.Min.X < s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y < s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X > s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y > s.Max.Y { + r.Max.Y = s.Max.Y + } + // Letting r0 and s0 be the values of r and s at the time that the method + // is called, this next line is equivalent to: + // + // if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc } + if r.Empty() { + return Rectangle26_6{} + } + return r +} + +// Union returns the smallest rectangle that contains both r and s. +func (r Rectangle26_6) Union(s Rectangle26_6) Rectangle26_6 { + if r.Empty() { + return s + } + if s.Empty() { + return r + } + if r.Min.X > s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y > s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X < s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y < s.Max.Y { + r.Max.Y = s.Max.Y + } + return r +} + +// Empty returns whether the rectangle contains no points. +func (r Rectangle26_6) Empty() bool { + return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y +} + +// In returns whether every point in r is in s. +func (r Rectangle26_6) In(s Rectangle26_6) bool { + if r.Empty() { + return true + } + // Note that r.Max is an exclusive bound for r, so that r.In(s) + // does not require that r.Max.In(s). + return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && + s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y +} + +// Rectangle52_12 is a 52.12 fixed-point coordinate rectangle. The Min bound is +// inclusive and the Max bound is exclusive. It is well-formed if Min.X <= +// Max.X and likewise for Y. +// +// It is analogous to the image.Rectangle type in the standard library. +type Rectangle52_12 struct { + Min, Max Point52_12 +} + +// Add returns the rectangle r translated by p. +func (r Rectangle52_12) Add(p Point52_12) Rectangle52_12 { + return Rectangle52_12{ + Point52_12{r.Min.X + p.X, r.Min.Y + p.Y}, + Point52_12{r.Max.X + p.X, r.Max.Y + p.Y}, + } +} + +// Sub returns the rectangle r translated by -p. +func (r Rectangle52_12) Sub(p Point52_12) Rectangle52_12 { + return Rectangle52_12{ + Point52_12{r.Min.X - p.X, r.Min.Y - p.Y}, + Point52_12{r.Max.X - p.X, r.Max.Y - p.Y}, + } +} + +// Intersect returns the largest rectangle contained by both r and s. If the +// two rectangles do not overlap then the zero rectangle will be returned. +func (r Rectangle52_12) Intersect(s Rectangle52_12) Rectangle52_12 { + if r.Min.X < s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y < s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X > s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y > s.Max.Y { + r.Max.Y = s.Max.Y + } + // Letting r0 and s0 be the values of r and s at the time that the method + // is called, this next line is equivalent to: + // + // if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc } + if r.Empty() { + return Rectangle52_12{} + } + return r +} + +// Union returns the smallest rectangle that contains both r and s. +func (r Rectangle52_12) Union(s Rectangle52_12) Rectangle52_12 { + if r.Empty() { + return s + } + if s.Empty() { + return r + } + if r.Min.X > s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y > s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X < s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y < s.Max.Y { + r.Max.Y = s.Max.Y + } + return r +} + +// Empty returns whether the rectangle contains no points. +func (r Rectangle52_12) Empty() bool { + return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y +} + +// In returns whether every point in r is in s. +func (r Rectangle52_12) In(s Rectangle52_12) bool { + if r.Empty() { + return true + } + // Note that r.Max is an exclusive bound for r, so that r.In(s) + // does not require that r.Max.In(s). + return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && + s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y +} diff --git a/vendor/golang.org/x/image/vector/acc_amd64.go b/vendor/golang.org/x/image/vector/acc_amd64.go new file mode 100644 index 0000000000000..c0d6ad95b4399 --- /dev/null +++ b/vendor/golang.org/x/image/vector/acc_amd64.go @@ -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. + +// +build !appengine +// +build gc +// +build !noasm + +package vector + +func haveSSE4_1() bool + +var haveAccumulateSIMD = haveSSE4_1() + +//go:noescape +func fixedAccumulateOpOverSIMD(dst []uint8, src []uint32) + +//go:noescape +func fixedAccumulateOpSrcSIMD(dst []uint8, src []uint32) + +//go:noescape +func fixedAccumulateMaskSIMD(buf []uint32) + +//go:noescape +func floatingAccumulateOpOverSIMD(dst []uint8, src []float32) + +//go:noescape +func floatingAccumulateOpSrcSIMD(dst []uint8, src []float32) + +//go:noescape +func floatingAccumulateMaskSIMD(dst []uint32, src []float32) diff --git a/vendor/golang.org/x/image/vector/acc_amd64.s b/vendor/golang.org/x/image/vector/acc_amd64.s new file mode 100644 index 0000000000000..fc6e7f837cc61 --- /dev/null +++ b/vendor/golang.org/x/image/vector/acc_amd64.s @@ -0,0 +1,1028 @@ +// generated by go run gen.go; DO NOT EDIT + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +// fl is short for floating point math. fx is short for fixed point math. + +DATA flAlmost65536<>+0x00(SB)/8, $0x477fffff477fffff +DATA flAlmost65536<>+0x08(SB)/8, $0x477fffff477fffff +DATA flOne<>+0x00(SB)/8, $0x3f8000003f800000 +DATA flOne<>+0x08(SB)/8, $0x3f8000003f800000 +DATA flSignMask<>+0x00(SB)/8, $0x7fffffff7fffffff +DATA flSignMask<>+0x08(SB)/8, $0x7fffffff7fffffff + +// scatterAndMulBy0x101 is a PSHUFB mask that brings the low four bytes of an +// XMM register to the low byte of that register's four uint32 values. It +// duplicates those bytes, effectively multiplying each uint32 by 0x101. +// +// It transforms a little-endian 16-byte XMM value from +// ijkl???????????? +// to +// ii00jj00kk00ll00 +DATA scatterAndMulBy0x101<>+0x00(SB)/8, $0x8080010180800000 +DATA scatterAndMulBy0x101<>+0x08(SB)/8, $0x8080030380800202 + +// gather is a PSHUFB mask that brings the second-lowest byte of the XMM +// register's four uint32 values to the low four bytes of that register. +// +// It transforms a little-endian 16-byte XMM value from +// ?i???j???k???l?? +// to +// ijkl000000000000 +DATA gather<>+0x00(SB)/8, $0x808080800d090501 +DATA gather<>+0x08(SB)/8, $0x8080808080808080 + +DATA fxAlmost65536<>+0x00(SB)/8, $0x0000ffff0000ffff +DATA fxAlmost65536<>+0x08(SB)/8, $0x0000ffff0000ffff +DATA inverseFFFF<>+0x00(SB)/8, $0x8000800180008001 +DATA inverseFFFF<>+0x08(SB)/8, $0x8000800180008001 + +GLOBL flAlmost65536<>(SB), (NOPTR+RODATA), $16 +GLOBL flOne<>(SB), (NOPTR+RODATA), $16 +GLOBL flSignMask<>(SB), (NOPTR+RODATA), $16 +GLOBL scatterAndMulBy0x101<>(SB), (NOPTR+RODATA), $16 +GLOBL gather<>(SB), (NOPTR+RODATA), $16 +GLOBL fxAlmost65536<>(SB), (NOPTR+RODATA), $16 +GLOBL inverseFFFF<>(SB), (NOPTR+RODATA), $16 + +// func haveSSE4_1() bool +TEXT ·haveSSE4_1(SB), NOSPLIT, $0 + MOVQ $1, AX + CPUID + SHRQ $19, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + +// ---------------------------------------------------------------------------- + +// func fixedAccumulateOpOverSIMD(dst []uint8, src []uint32) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 - +// xmm4 - +// xmm5 fxAlmost65536 +// xmm6 gather +// xmm7 offset +// xmm8 scatterAndMulBy0x101 +// xmm9 fxAlmost65536 +// xmm10 inverseFFFF +TEXT ·fixedAccumulateOpOverSIMD(SB), NOSPLIT, $0-48 + + MOVQ dst_base+0(FP), DI + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R10 + + // Sanity check that len(dst) >= len(src). + CMPQ BX, R10 + JLT fxAccOpOverEnd + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + // fxAlmost65536 := XMM(0x0000ffff repeated four times) // Maximum of an uint16. + MOVOU fxAlmost65536<>(SB), X5 + + // gather := XMM(see above) // PSHUFB shuffle mask. + // scatterAndMulBy0x101 := XMM(see above) // PSHUFB shuffle mask. + // fxAlmost65536 := XMM(0x0000ffff repeated four times) // 0xffff. + // inverseFFFF := XMM(0x80008001 repeated four times) // Magic constant for dividing by 0xffff. + MOVOU gather<>(SB), X6 + MOVOU scatterAndMulBy0x101<>(SB), X8 + MOVOU fxAlmost65536<>(SB), X9 + MOVOU inverseFFFF<>(SB), X10 + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +fxAccOpOverLoop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE fxAccOpOverLoop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + PADDD X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + PADDD X0, X1 + + // x += offset + PADDD X7, X1 + + // y = abs(x) + // y >>= 2 // Shift by 2*Ï• - 16. + // y = min(y, fxAlmost65536) + PABSD X1, X2 + PSRLL $2, X2 + PMINUD X5, X2 + + // z = convertToInt32(y) + // No-op. + + // Blend over the dst's prior value. SIMD for i in 0..3: + // + // dstA := uint32(dst[i]) * 0x101 + // maskA := z@i + // outA := dstA*(0xffff-maskA)/0xffff + maskA + // dst[i] = uint8(outA >> 8) + // + // First, set X0 to dstA*(0xfff-maskA). + MOVL (DI), X0 + PSHUFB X8, X0 + MOVOU X9, X11 + PSUBL X2, X11 + PMULLD X11, X0 + + // We implement uint32 division by 0xffff as multiplication by a magic + // constant (0x800080001) and then a shift by a magic constant (47). + // See TestDivideByFFFF for a justification. + // + // That multiplication widens from uint32 to uint64, so we have to + // duplicate and shift our four uint32s from one XMM register (X0) to + // two XMM registers (X0 and X11). + // + // Move the second and fourth uint32s in X0 to be the first and third + // uint32s in X11. + MOVOU X0, X11 + PSRLQ $32, X11 + + // Multiply by magic, shift by magic. + PMULULQ X10, X0 + PMULULQ X10, X11 + PSRLQ $47, X0 + PSRLQ $47, X11 + + // Merge the two registers back to one, X11, and add maskA. + PSLLQ $32, X11 + XORPS X0, X11 + PADDD X11, X2 + + // As per opSrcStore4, shuffle and copy the 4 second-lowest bytes. + PSHUFB X6, X2 + MOVL X2, (DI) + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ $4, DI + ADDQ $16, SI + JMP fxAccOpOverLoop4 + +fxAccOpOverLoop1: + // for i < len(src) + CMPQ R9, R11 + JAE fxAccOpOverEnd + + // x = src[i] + offset + MOVL (SI), X1 + PADDD X7, X1 + + // y = abs(x) + // y >>= 2 // Shift by 2*Ï• - 16. + // y = min(y, fxAlmost65536) + PABSD X1, X2 + PSRLL $2, X2 + PMINUD X5, X2 + + // z = convertToInt32(y) + // No-op. + + // Blend over the dst's prior value. + // + // dstA := uint32(dst[0]) * 0x101 + // maskA := z + // outA := dstA*(0xffff-maskA)/0xffff + maskA + // dst[0] = uint8(outA >> 8) + MOVBLZX (DI), R12 + IMULL $0x101, R12 + MOVL X2, R13 + MOVL $0xffff, AX + SUBL R13, AX + MULL R12 // MULL's implicit arg is AX, and the result is stored in DX:AX. + MOVL $0x80008001, BX // Divide by 0xffff is to first multiply by a magic constant... + MULL BX // MULL's implicit arg is AX, and the result is stored in DX:AX. + SHRL $15, DX // ...and then shift by another magic constant (47 - 32 = 15). + ADDL DX, R13 + SHRL $8, R13 + MOVB R13, (DI) + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ $1, DI + ADDQ $4, SI + JMP fxAccOpOverLoop1 + +fxAccOpOverEnd: + RET + +// ---------------------------------------------------------------------------- + +// func fixedAccumulateOpSrcSIMD(dst []uint8, src []uint32) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 - +// xmm4 - +// xmm5 fxAlmost65536 +// xmm6 gather +// xmm7 offset +// xmm8 - +// xmm9 - +// xmm10 - +TEXT ·fixedAccumulateOpSrcSIMD(SB), NOSPLIT, $0-48 + + MOVQ dst_base+0(FP), DI + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R10 + + // Sanity check that len(dst) >= len(src). + CMPQ BX, R10 + JLT fxAccOpSrcEnd + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + // fxAlmost65536 := XMM(0x0000ffff repeated four times) // Maximum of an uint16. + MOVOU fxAlmost65536<>(SB), X5 + + // gather := XMM(see above) // PSHUFB shuffle mask. + MOVOU gather<>(SB), X6 + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +fxAccOpSrcLoop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE fxAccOpSrcLoop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + PADDD X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + PADDD X0, X1 + + // x += offset + PADDD X7, X1 + + // y = abs(x) + // y >>= 2 // Shift by 2*Ï• - 16. + // y = min(y, fxAlmost65536) + PABSD X1, X2 + PSRLL $2, X2 + PMINUD X5, X2 + + // z = convertToInt32(y) + // No-op. + + // z = shuffleTheSecondLowestBytesOfEach4ByteElement(z) + // copy(dst[:4], low4BytesOf(z)) + PSHUFB X6, X2 + MOVL X2, (DI) + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ $4, DI + ADDQ $16, SI + JMP fxAccOpSrcLoop4 + +fxAccOpSrcLoop1: + // for i < len(src) + CMPQ R9, R11 + JAE fxAccOpSrcEnd + + // x = src[i] + offset + MOVL (SI), X1 + PADDD X7, X1 + + // y = abs(x) + // y >>= 2 // Shift by 2*Ï• - 16. + // y = min(y, fxAlmost65536) + PABSD X1, X2 + PSRLL $2, X2 + PMINUD X5, X2 + + // z = convertToInt32(y) + // No-op. + + // dst[0] = uint8(z>>8) + MOVL X2, BX + SHRL $8, BX + MOVB BX, (DI) + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ $1, DI + ADDQ $4, SI + JMP fxAccOpSrcLoop1 + +fxAccOpSrcEnd: + RET + +// ---------------------------------------------------------------------------- + +// func fixedAccumulateMaskSIMD(buf []uint32) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 - +// xmm4 - +// xmm5 fxAlmost65536 +// xmm6 - +// xmm7 offset +// xmm8 - +// xmm9 - +// xmm10 - +TEXT ·fixedAccumulateMaskSIMD(SB), NOSPLIT, $0-24 + + MOVQ buf_base+0(FP), DI + MOVQ buf_len+8(FP), BX + MOVQ buf_base+0(FP), SI + MOVQ buf_len+8(FP), R10 + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + // fxAlmost65536 := XMM(0x0000ffff repeated four times) // Maximum of an uint16. + MOVOU fxAlmost65536<>(SB), X5 + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +fxAccMaskLoop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE fxAccMaskLoop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + PADDD X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + PADDD X0, X1 + + // x += offset + PADDD X7, X1 + + // y = abs(x) + // y >>= 2 // Shift by 2*Ï• - 16. + // y = min(y, fxAlmost65536) + PABSD X1, X2 + PSRLL $2, X2 + PMINUD X5, X2 + + // z = convertToInt32(y) + // No-op. + + // copy(dst[:4], z) + MOVOU X2, (DI) + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ $16, DI + ADDQ $16, SI + JMP fxAccMaskLoop4 + +fxAccMaskLoop1: + // for i < len(src) + CMPQ R9, R11 + JAE fxAccMaskEnd + + // x = src[i] + offset + MOVL (SI), X1 + PADDD X7, X1 + + // y = abs(x) + // y >>= 2 // Shift by 2*Ï• - 16. + // y = min(y, fxAlmost65536) + PABSD X1, X2 + PSRLL $2, X2 + PMINUD X5, X2 + + // z = convertToInt32(y) + // No-op. + + // dst[0] = uint32(z) + MOVL X2, (DI) + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ $4, DI + ADDQ $4, SI + JMP fxAccMaskLoop1 + +fxAccMaskEnd: + RET + +// ---------------------------------------------------------------------------- + +// func floatingAccumulateOpOverSIMD(dst []uint8, src []float32) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 flSignMask +// xmm4 flOne +// xmm5 flAlmost65536 +// xmm6 gather +// xmm7 offset +// xmm8 scatterAndMulBy0x101 +// xmm9 fxAlmost65536 +// xmm10 inverseFFFF +TEXT ·floatingAccumulateOpOverSIMD(SB), NOSPLIT, $8-48 + + MOVQ dst_base+0(FP), DI + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R10 + + // Sanity check that len(dst) >= len(src). + CMPQ BX, R10 + JLT flAccOpOverEnd + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + // Prepare to set MXCSR bits 13 and 14, so that the CVTPS2PL below is + // "Round To Zero". + STMXCSR mxcsrOrig-8(SP) + MOVL mxcsrOrig-8(SP), AX + ORL $0x6000, AX + MOVL AX, mxcsrNew-4(SP) + + // flSignMask := XMM(0x7fffffff repeated four times) // All but the sign bit of a float32. + // flOne := XMM(0x3f800000 repeated four times) // 1 as a float32. + // flAlmost65536 := XMM(0x477fffff repeated four times) // 255.99998 * 256 as a float32. + MOVOU flSignMask<>(SB), X3 + MOVOU flOne<>(SB), X4 + MOVOU flAlmost65536<>(SB), X5 + + // gather := XMM(see above) // PSHUFB shuffle mask. + // scatterAndMulBy0x101 := XMM(see above) // PSHUFB shuffle mask. + // fxAlmost65536 := XMM(0x0000ffff repeated four times) // 0xffff. + // inverseFFFF := XMM(0x80008001 repeated four times) // Magic constant for dividing by 0xffff. + MOVOU gather<>(SB), X6 + MOVOU scatterAndMulBy0x101<>(SB), X8 + MOVOU fxAlmost65536<>(SB), X9 + MOVOU inverseFFFF<>(SB), X10 + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +flAccOpOverLoop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE flAccOpOverLoop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + ADDPS X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + ADDPS X0, X1 + + // x += offset + ADDPS X7, X1 + + // y = x & flSignMask + // y = min(y, flOne) + // y = mul(y, flAlmost65536) + MOVOU X3, X2 + ANDPS X1, X2 + MINPS X4, X2 + MULPS X5, X2 + + // z = convertToInt32(y) + LDMXCSR mxcsrNew-4(SP) + CVTPS2PL X2, X2 + LDMXCSR mxcsrOrig-8(SP) + + // Blend over the dst's prior value. SIMD for i in 0..3: + // + // dstA := uint32(dst[i]) * 0x101 + // maskA := z@i + // outA := dstA*(0xffff-maskA)/0xffff + maskA + // dst[i] = uint8(outA >> 8) + // + // First, set X0 to dstA*(0xfff-maskA). + MOVL (DI), X0 + PSHUFB X8, X0 + MOVOU X9, X11 + PSUBL X2, X11 + PMULLD X11, X0 + + // We implement uint32 division by 0xffff as multiplication by a magic + // constant (0x800080001) and then a shift by a magic constant (47). + // See TestDivideByFFFF for a justification. + // + // That multiplication widens from uint32 to uint64, so we have to + // duplicate and shift our four uint32s from one XMM register (X0) to + // two XMM registers (X0 and X11). + // + // Move the second and fourth uint32s in X0 to be the first and third + // uint32s in X11. + MOVOU X0, X11 + PSRLQ $32, X11 + + // Multiply by magic, shift by magic. + PMULULQ X10, X0 + PMULULQ X10, X11 + PSRLQ $47, X0 + PSRLQ $47, X11 + + // Merge the two registers back to one, X11, and add maskA. + PSLLQ $32, X11 + XORPS X0, X11 + PADDD X11, X2 + + // As per opSrcStore4, shuffle and copy the 4 second-lowest bytes. + PSHUFB X6, X2 + MOVL X2, (DI) + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ $4, DI + ADDQ $16, SI + JMP flAccOpOverLoop4 + +flAccOpOverLoop1: + // for i < len(src) + CMPQ R9, R11 + JAE flAccOpOverEnd + + // x = src[i] + offset + MOVL (SI), X1 + ADDPS X7, X1 + + // y = x & flSignMask + // y = min(y, flOne) + // y = mul(y, flAlmost65536) + MOVOU X3, X2 + ANDPS X1, X2 + MINPS X4, X2 + MULPS X5, X2 + + // z = convertToInt32(y) + LDMXCSR mxcsrNew-4(SP) + CVTPS2PL X2, X2 + LDMXCSR mxcsrOrig-8(SP) + + // Blend over the dst's prior value. + // + // dstA := uint32(dst[0]) * 0x101 + // maskA := z + // outA := dstA*(0xffff-maskA)/0xffff + maskA + // dst[0] = uint8(outA >> 8) + MOVBLZX (DI), R12 + IMULL $0x101, R12 + MOVL X2, R13 + MOVL $0xffff, AX + SUBL R13, AX + MULL R12 // MULL's implicit arg is AX, and the result is stored in DX:AX. + MOVL $0x80008001, BX // Divide by 0xffff is to first multiply by a magic constant... + MULL BX // MULL's implicit arg is AX, and the result is stored in DX:AX. + SHRL $15, DX // ...and then shift by another magic constant (47 - 32 = 15). + ADDL DX, R13 + SHRL $8, R13 + MOVB R13, (DI) + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ $1, DI + ADDQ $4, SI + JMP flAccOpOverLoop1 + +flAccOpOverEnd: + RET + +// ---------------------------------------------------------------------------- + +// func floatingAccumulateOpSrcSIMD(dst []uint8, src []float32) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 flSignMask +// xmm4 flOne +// xmm5 flAlmost65536 +// xmm6 gather +// xmm7 offset +// xmm8 - +// xmm9 - +// xmm10 - +TEXT ·floatingAccumulateOpSrcSIMD(SB), NOSPLIT, $8-48 + + MOVQ dst_base+0(FP), DI + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R10 + + // Sanity check that len(dst) >= len(src). + CMPQ BX, R10 + JLT flAccOpSrcEnd + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + // Prepare to set MXCSR bits 13 and 14, so that the CVTPS2PL below is + // "Round To Zero". + STMXCSR mxcsrOrig-8(SP) + MOVL mxcsrOrig-8(SP), AX + ORL $0x6000, AX + MOVL AX, mxcsrNew-4(SP) + + // flSignMask := XMM(0x7fffffff repeated four times) // All but the sign bit of a float32. + // flOne := XMM(0x3f800000 repeated four times) // 1 as a float32. + // flAlmost65536 := XMM(0x477fffff repeated four times) // 255.99998 * 256 as a float32. + MOVOU flSignMask<>(SB), X3 + MOVOU flOne<>(SB), X4 + MOVOU flAlmost65536<>(SB), X5 + + // gather := XMM(see above) // PSHUFB shuffle mask. + MOVOU gather<>(SB), X6 + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +flAccOpSrcLoop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE flAccOpSrcLoop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + ADDPS X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + ADDPS X0, X1 + + // x += offset + ADDPS X7, X1 + + // y = x & flSignMask + // y = min(y, flOne) + // y = mul(y, flAlmost65536) + MOVOU X3, X2 + ANDPS X1, X2 + MINPS X4, X2 + MULPS X5, X2 + + // z = convertToInt32(y) + LDMXCSR mxcsrNew-4(SP) + CVTPS2PL X2, X2 + LDMXCSR mxcsrOrig-8(SP) + + // z = shuffleTheSecondLowestBytesOfEach4ByteElement(z) + // copy(dst[:4], low4BytesOf(z)) + PSHUFB X6, X2 + MOVL X2, (DI) + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ $4, DI + ADDQ $16, SI + JMP flAccOpSrcLoop4 + +flAccOpSrcLoop1: + // for i < len(src) + CMPQ R9, R11 + JAE flAccOpSrcEnd + + // x = src[i] + offset + MOVL (SI), X1 + ADDPS X7, X1 + + // y = x & flSignMask + // y = min(y, flOne) + // y = mul(y, flAlmost65536) + MOVOU X3, X2 + ANDPS X1, X2 + MINPS X4, X2 + MULPS X5, X2 + + // z = convertToInt32(y) + LDMXCSR mxcsrNew-4(SP) + CVTPS2PL X2, X2 + LDMXCSR mxcsrOrig-8(SP) + + // dst[0] = uint8(z>>8) + MOVL X2, BX + SHRL $8, BX + MOVB BX, (DI) + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ $1, DI + ADDQ $4, SI + JMP flAccOpSrcLoop1 + +flAccOpSrcEnd: + RET + +// ---------------------------------------------------------------------------- + +// func floatingAccumulateMaskSIMD(dst []uint32, src []float32) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 flSignMask +// xmm4 flOne +// xmm5 flAlmost65536 +// xmm6 - +// xmm7 offset +// xmm8 - +// xmm9 - +// xmm10 - +TEXT ·floatingAccumulateMaskSIMD(SB), NOSPLIT, $8-48 + + MOVQ dst_base+0(FP), DI + MOVQ dst_len+8(FP), BX + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R10 + + // Sanity check that len(dst) >= len(src). + CMPQ BX, R10 + JLT flAccMaskEnd + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + // Prepare to set MXCSR bits 13 and 14, so that the CVTPS2PL below is + // "Round To Zero". + STMXCSR mxcsrOrig-8(SP) + MOVL mxcsrOrig-8(SP), AX + ORL $0x6000, AX + MOVL AX, mxcsrNew-4(SP) + + // flSignMask := XMM(0x7fffffff repeated four times) // All but the sign bit of a float32. + // flOne := XMM(0x3f800000 repeated four times) // 1 as a float32. + // flAlmost65536 := XMM(0x477fffff repeated four times) // 255.99998 * 256 as a float32. + MOVOU flSignMask<>(SB), X3 + MOVOU flOne<>(SB), X4 + MOVOU flAlmost65536<>(SB), X5 + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +flAccMaskLoop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE flAccMaskLoop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + ADDPS X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + ADDPS X0, X1 + + // x += offset + ADDPS X7, X1 + + // y = x & flSignMask + // y = min(y, flOne) + // y = mul(y, flAlmost65536) + MOVOU X3, X2 + ANDPS X1, X2 + MINPS X4, X2 + MULPS X5, X2 + + // z = convertToInt32(y) + LDMXCSR mxcsrNew-4(SP) + CVTPS2PL X2, X2 + LDMXCSR mxcsrOrig-8(SP) + + // copy(dst[:4], z) + MOVOU X2, (DI) + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ $16, DI + ADDQ $16, SI + JMP flAccMaskLoop4 + +flAccMaskLoop1: + // for i < len(src) + CMPQ R9, R11 + JAE flAccMaskEnd + + // x = src[i] + offset + MOVL (SI), X1 + ADDPS X7, X1 + + // y = x & flSignMask + // y = min(y, flOne) + // y = mul(y, flAlmost65536) + MOVOU X3, X2 + ANDPS X1, X2 + MINPS X4, X2 + MULPS X5, X2 + + // z = convertToInt32(y) + LDMXCSR mxcsrNew-4(SP) + CVTPS2PL X2, X2 + LDMXCSR mxcsrOrig-8(SP) + + // dst[0] = uint32(z) + MOVL X2, (DI) + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ $4, DI + ADDQ $4, SI + JMP flAccMaskLoop1 + +flAccMaskEnd: + RET diff --git a/vendor/golang.org/x/image/vector/acc_other.go b/vendor/golang.org/x/image/vector/acc_other.go new file mode 100644 index 0000000000000..d00bed843dfc6 --- /dev/null +++ b/vendor/golang.org/x/image/vector/acc_other.go @@ -0,0 +1,16 @@ +// 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. + +// +build !amd64 appengine !gc noasm + +package vector + +const haveAccumulateSIMD = false + +func fixedAccumulateOpOverSIMD(dst []uint8, src []uint32) {} +func fixedAccumulateOpSrcSIMD(dst []uint8, src []uint32) {} +func fixedAccumulateMaskSIMD(buf []uint32) {} +func floatingAccumulateOpOverSIMD(dst []uint8, src []float32) {} +func floatingAccumulateOpSrcSIMD(dst []uint8, src []float32) {} +func floatingAccumulateMaskSIMD(dst []uint32, src []float32) {} diff --git a/vendor/golang.org/x/image/vector/gen_acc_amd64.s.tmpl b/vendor/golang.org/x/image/vector/gen_acc_amd64.s.tmpl new file mode 100644 index 0000000000000..05ce25bb5e382 --- /dev/null +++ b/vendor/golang.org/x/image/vector/gen_acc_amd64.s.tmpl @@ -0,0 +1,170 @@ +// 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. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +// fl is short for floating point math. fx is short for fixed point math. + +DATA flAlmost65536<>+0x00(SB)/8, $0x477fffff477fffff +DATA flAlmost65536<>+0x08(SB)/8, $0x477fffff477fffff +DATA flOne<>+0x00(SB)/8, $0x3f8000003f800000 +DATA flOne<>+0x08(SB)/8, $0x3f8000003f800000 +DATA flSignMask<>+0x00(SB)/8, $0x7fffffff7fffffff +DATA flSignMask<>+0x08(SB)/8, $0x7fffffff7fffffff + +// scatterAndMulBy0x101 is a PSHUFB mask that brings the low four bytes of an +// XMM register to the low byte of that register's four uint32 values. It +// duplicates those bytes, effectively multiplying each uint32 by 0x101. +// +// It transforms a little-endian 16-byte XMM value from +// ijkl???????????? +// to +// ii00jj00kk00ll00 +DATA scatterAndMulBy0x101<>+0x00(SB)/8, $0x8080010180800000 +DATA scatterAndMulBy0x101<>+0x08(SB)/8, $0x8080030380800202 + +// gather is a PSHUFB mask that brings the second-lowest byte of the XMM +// register's four uint32 values to the low four bytes of that register. +// +// It transforms a little-endian 16-byte XMM value from +// ?i???j???k???l?? +// to +// ijkl000000000000 +DATA gather<>+0x00(SB)/8, $0x808080800d090501 +DATA gather<>+0x08(SB)/8, $0x8080808080808080 + +DATA fxAlmost65536<>+0x00(SB)/8, $0x0000ffff0000ffff +DATA fxAlmost65536<>+0x08(SB)/8, $0x0000ffff0000ffff +DATA inverseFFFF<>+0x00(SB)/8, $0x8000800180008001 +DATA inverseFFFF<>+0x08(SB)/8, $0x8000800180008001 + +GLOBL flAlmost65536<>(SB), (NOPTR+RODATA), $16 +GLOBL flOne<>(SB), (NOPTR+RODATA), $16 +GLOBL flSignMask<>(SB), (NOPTR+RODATA), $16 +GLOBL scatterAndMulBy0x101<>(SB), (NOPTR+RODATA), $16 +GLOBL gather<>(SB), (NOPTR+RODATA), $16 +GLOBL fxAlmost65536<>(SB), (NOPTR+RODATA), $16 +GLOBL inverseFFFF<>(SB), (NOPTR+RODATA), $16 + +// func haveSSE4_1() bool +TEXT ·haveSSE4_1(SB), NOSPLIT, $0 + MOVQ $1, AX + CPUID + SHRQ $19, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + +// ---------------------------------------------------------------------------- + +// func {{.LongName}}SIMD({{.Args}}) +// +// XMM registers. Variable names are per +// https://github.com/google/font-rs/blob/master/src/accumulate.c +// +// xmm0 scratch +// xmm1 x +// xmm2 y, z +// xmm3 {{.XMM3}} +// xmm4 {{.XMM4}} +// xmm5 {{.XMM5}} +// xmm6 {{.XMM6}} +// xmm7 offset +// xmm8 {{.XMM8}} +// xmm9 {{.XMM9}} +// xmm10 {{.XMM10}} +TEXT ·{{.LongName}}SIMD(SB), NOSPLIT, ${{.FrameSize}}-{{.ArgsSize}} + {{.LoadArgs}} + + // R10 = len(src) &^ 3 + // R11 = len(src) + MOVQ R10, R11 + ANDQ $-4, R10 + + {{.Setup}} + + {{.LoadXMMRegs}} + + // offset := XMM(0x00000000 repeated four times) // Cumulative sum. + XORPS X7, X7 + + // i := 0 + MOVQ $0, R9 + +{{.ShortName}}Loop4: + // for i < (len(src) &^ 3) + CMPQ R9, R10 + JAE {{.ShortName}}Loop1 + + // x = XMM(s0, s1, s2, s3) + // + // Where s0 is src[i+0], s1 is src[i+1], etc. + MOVOU (SI), X1 + + // scratch = XMM(0, s0, s1, s2) + // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) + MOVOU X1, X0 + PSLLO $4, X0 + {{.Add}} X0, X1 + + // scratch = XMM(0, 0, 0, 0) + // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) + // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) + XORPS X0, X0 + SHUFPS $0x40, X1, X0 + {{.Add}} X0, X1 + + // x += offset + {{.Add}} X7, X1 + + {{.ClampAndScale}} + + {{.ConvertToInt32}} + + {{.Store4}} + + // offset = XMM(x@3, x@3, x@3, x@3) + MOVOU X1, X7 + SHUFPS $0xff, X1, X7 + + // i += 4 + // dst = dst[4:] + // src = src[4:] + ADDQ $4, R9 + ADDQ ${{.DstElemSize4}}, DI + ADDQ $16, SI + JMP {{.ShortName}}Loop4 + +{{.ShortName}}Loop1: + // for i < len(src) + CMPQ R9, R11 + JAE {{.ShortName}}End + + // x = src[i] + offset + MOVL (SI), X1 + {{.Add}} X7, X1 + + {{.ClampAndScale}} + + {{.ConvertToInt32}} + + {{.Store1}} + + // offset = x + MOVOU X1, X7 + + // i += 1 + // dst = dst[1:] + // src = src[1:] + ADDQ $1, R9 + ADDQ ${{.DstElemSize1}}, DI + ADDQ $4, SI + JMP {{.ShortName}}Loop1 + +{{.ShortName}}End: + RET diff --git a/vendor/golang.org/x/image/vector/raster_fixed.go b/vendor/golang.org/x/image/vector/raster_fixed.go new file mode 100644 index 0000000000000..5b0fe7a7ebfb3 --- /dev/null +++ b/vendor/golang.org/x/image/vector/raster_fixed.go @@ -0,0 +1,327 @@ +// 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 vector + +// This file contains a fixed point math implementation of the vector +// graphics rasterizer. + +const ( + // Ï• is the number of binary digits after the fixed point. + // + // For example, if Ï• == 10 (and int1Ï• is based on the int32 type) then we + // are using 22.10 fixed point math. + // + // When changing this number, also change the assembly code (search for Ï• + // in the .s files). + Ï• = 9 + + fxOne int1Ï• = 1 << Ï• + fxOneAndAHalf int1Ï• = 1<<Ï• + 1<<(Ï•-1) + fxOneMinusIota int1Ï• = 1<<Ï• - 1 // Used for rounding up. +) + +// int1Ï• is a signed fixed-point number with 1*Ï• binary digits after the fixed +// point. +type int1Ï• int32 + +// int2Ï• is a signed fixed-point number with 2*Ï• binary digits after the fixed +// point. +// +// The Rasterizer's bufU32 field, nominally of type []uint32 (since that slice +// is also used by other code), can be thought of as a []int2Ï• during the +// fixedLineTo method. Lines of code that are actually like: +// buf[i] += uint32(etc) // buf has type []uint32. +// can be thought of as +// buf[i] += int2Ï•(etc) // buf has type []int2Ï•. +type int2Ï• int32 + +func fixedMax(x, y int1Ï•) int1Ï• { + if x > y { + return x + } + return y +} + +func fixedMin(x, y int1Ï•) int1Ï• { + if x < y { + return x + } + return y +} + +func fixedFloor(x int1Ï•) int32 { return int32(x >> Ï•) } +func fixedCeil(x int1Ï•) int32 { return int32((x + fxOneMinusIota) >> Ï•) } + +func (z *Rasterizer) fixedLineTo(bx, by float32) { + ax, ay := z.penX, z.penY + z.penX, z.penY = bx, by + dir := int1Ï•(1) + if ay > by { + dir, ax, ay, bx, by = -1, bx, by, ax, ay + } + // Horizontal line segments yield no change in coverage. Almost horizontal + // segments would yield some change, in ideal math, but the computation + // further below, involving 1 / (by - ay), is unstable in fixed point math, + // so we treat the segment as if it was perfectly horizontal. + if by-ay <= 0.000001 { + return + } + dxdy := (bx - ax) / (by - ay) + + ayÏ• := int1Ï•(ay * float32(fxOne)) + byÏ• := int1Ï•(by * float32(fxOne)) + + x := int1Ï•(ax * float32(fxOne)) + y := fixedFloor(ayÏ•) + yMax := fixedCeil(byÏ•) + if yMax > int32(z.size.Y) { + yMax = int32(z.size.Y) + } + width := int32(z.size.X) + + for ; y < yMax; y++ { + dy := fixedMin(int1Ï•(y+1)<<Ï•, byÏ•) - fixedMax(int1Ï•(y)<<Ï•, ayÏ•) + xNext := x + int1Ï•(float32(dy)*dxdy) + if y < 0 { + x = xNext + continue + } + buf := z.bufU32[y*width:] + d := dy * dir // d ranges up to ±1<<(1*Ï•). + x0, x1 := x, xNext + if x > xNext { + x0, x1 = x1, x0 + } + x0i := fixedFloor(x0) + x0Floor := int1Ï•(x0i) << Ï• + x1i := fixedCeil(x1) + x1Ceil := int1Ï•(x1i) << Ï• + + if x1i <= x0i+1 { + xmf := (x+xNext)>>1 - x0Floor + if i := clamp(x0i+0, width); i < uint(len(buf)) { + buf[i] += uint32(d * (fxOne - xmf)) + } + if i := clamp(x0i+1, width); i < uint(len(buf)) { + buf[i] += uint32(d * xmf) + } + } else { + oneOverS := x1 - x0 + twoOverS := 2 * oneOverS + x0f := x0 - x0Floor + oneMinusX0f := fxOne - x0f + oneMinusX0fSquared := oneMinusX0f * oneMinusX0f + x1f := x1 - x1Ceil + fxOne + x1fSquared := x1f * x1f + + // These next two variables are unused, as rounding errors are + // minimized when we delay the division by oneOverS for as long as + // possible. These lines of code (and the "In ideal math" comments + // below) are commented out instead of deleted in order to aid the + // comparison with the floating point version of the rasterizer. + // + // a0 := ((oneMinusX0f * oneMinusX0f) >> 1) / oneOverS + // am := ((x1f * x1f) >> 1) / oneOverS + + if i := clamp(x0i, width); i < uint(len(buf)) { + // In ideal math: buf[i] += uint32(d * a0) + D := oneMinusX0fSquared // D ranges up to ±1<<(2*Ï•). + D *= d // D ranges up to ±1<<(3*Ï•). + D /= twoOverS + buf[i] += uint32(D) + } + + if x1i == x0i+2 { + if i := clamp(x0i+1, width); i < uint(len(buf)) { + // In ideal math: buf[i] += uint32(d * (fxOne - a0 - am)) + // + // (x1i == x0i+2) and (twoOverS == 2 * (x1 - x0)) implies + // that twoOverS ranges up to +1<<(1*Ï•+2). + D := twoOverS<<Ï• - oneMinusX0fSquared - x1fSquared // D ranges up to ±1<<(2*Ï•+2). + D *= d // D ranges up to ±1<<(3*Ï•+2). + D /= twoOverS + buf[i] += uint32(D) + } + } else { + // This is commented out for the same reason as a0 and am. + // + // a1 := ((fxOneAndAHalf - x0f) << Ï•) / oneOverS + + if i := clamp(x0i+1, width); i < uint(len(buf)) { + // In ideal math: + // buf[i] += uint32(d * (a1 - a0)) + // or equivalently (but better in non-ideal, integer math, + // with respect to rounding errors), + // buf[i] += uint32(A * d / twoOverS) + // where + // A = (a1 - a0) * twoOverS + // = a1*twoOverS - a0*twoOverS + // Noting that twoOverS/oneOverS equals 2, substituting for + // a0 and then a1, given above, yields: + // A = a1*twoOverS - oneMinusX0fSquared + // = (fxOneAndAHalf-x0f)<<(Ï•+1) - oneMinusX0fSquared + // = fxOneAndAHalf<<(Ï•+1) - x0f<<(Ï•+1) - oneMinusX0fSquared + // + // This is a positive number minus two non-negative + // numbers. For an upper bound on A, the positive number is + // P = fxOneAndAHalf<<(Ï•+1) + // < (2*fxOne)<<(Ï•+1) + // = fxOne<<(Ï•+2) + // = 1<<(2*Ï•+2) + // + // For a lower bound on A, the two non-negative numbers are + // N = x0f<<(Ï•+1) + oneMinusX0fSquared + // ≤ x0f<<(Ï•+1) + fxOne*fxOne + // = x0f<<(Ï•+1) + 1<<(2*Ï•) + // < x0f<<(Ï•+1) + 1<<(2*Ï•+1) + // ≤ fxOne<<(Ï•+1) + 1<<(2*Ï•+1) + // = 1<<(2*Ï•+1) + 1<<(2*Ï•+1) + // = 1<<(2*Ï•+2) + // + // Thus, A ranges up to ±1<<(2*Ï•+2). It is possible to + // derive a tighter bound, but this bound is sufficient to + // reason about overflow. + D := (fxOneAndAHalf-x0f)<<(Ï•+1) - oneMinusX0fSquared // D ranges up to ±1<<(2*Ï•+2). + D *= d // D ranges up to ±1<<(3*Ï•+2). + D /= twoOverS + buf[i] += uint32(D) + } + dTimesS := uint32((d << (2 * Ï•)) / oneOverS) + for xi := x0i + 2; xi < x1i-1; xi++ { + if i := clamp(xi, width); i < uint(len(buf)) { + buf[i] += dTimesS + } + } + + // This is commented out for the same reason as a0 and am. + // + // a2 := a1 + (int1Ï•(x1i-x0i-3)<<(2*Ï•))/oneOverS + + if i := clamp(x1i-1, width); i < uint(len(buf)) { + // In ideal math: + // buf[i] += uint32(d * (fxOne - a2 - am)) + // or equivalently (but better in non-ideal, integer math, + // with respect to rounding errors), + // buf[i] += uint32(A * d / twoOverS) + // where + // A = (fxOne - a2 - am) * twoOverS + // = twoOverS<<Ï• - a2*twoOverS - am*twoOverS + // Noting that twoOverS/oneOverS equals 2, substituting for + // am and then a2, given above, yields: + // A = twoOverS<<Ï• - a2*twoOverS - x1f*x1f + // = twoOverS<<Ï• - a1*twoOverS - (int1Ï•(x1i-x0i-3)<<(2*Ï•))*2 - x1f*x1f + // = twoOverS<<Ï• - a1*twoOverS - int1Ï•(x1i-x0i-3)<<(2*Ï•+1) - x1f*x1f + // Substituting for a1, given above, yields: + // A = twoOverS<<Ï• - ((fxOneAndAHalf-x0f)<<Ï•)*2 - int1Ï•(x1i-x0i-3)<<(2*Ï•+1) - x1f*x1f + // = twoOverS<<Ï• - (fxOneAndAHalf-x0f)<<(Ï•+1) - int1Ï•(x1i-x0i-3)<<(2*Ï•+1) - x1f*x1f + // = B<<Ï• - x1f*x1f + // where + // B = twoOverS - (fxOneAndAHalf-x0f)<<1 - int1Ï•(x1i-x0i-3)<<(Ï•+1) + // = (x1-x0)<<1 - (fxOneAndAHalf-x0f)<<1 - int1Ï•(x1i-x0i-3)<<(Ï•+1) + // + // Re-arranging the defintions given above: + // x0Floor := int1Ï•(x0i) << Ï• + // x0f := x0 - x0Floor + // x1Ceil := int1Ï•(x1i) << Ï• + // x1f := x1 - x1Ceil + fxOne + // combined with fxOne = 1<<Ï• yields: + // x0 = x0f + int1Ï•(x0i)<<Ï• + // x1 = x1f + int1Ï•(x1i-1)<<Ï• + // so that expanding (x1-x0) yields: + // B = (x1f-x0f + int1Ï•(x1i-x0i-1)<<Ï•)<<1 - (fxOneAndAHalf-x0f)<<1 - int1Ï•(x1i-x0i-3)<<(Ï•+1) + // = (x1f-x0f)<<1 + int1Ï•(x1i-x0i-1)<<(Ï•+1) - (fxOneAndAHalf-x0f)<<1 - int1Ï•(x1i-x0i-3)<<(Ï•+1) + // A large part of the second and fourth terms cancel: + // B = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 - int1Ï•(-2)<<(Ï•+1) + // = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 + 1<<(Ï•+2) + // = (x1f - fxOneAndAHalf)<<1 + 1<<(Ï•+2) + // The first term, (x1f - fxOneAndAHalf)<<1, is a negative + // number, bounded below by -fxOneAndAHalf<<1, which is + // greater than -fxOne<<2, or -1<<(Ï•+2). Thus, B ranges up + // to ±1<<(Ï•+2). One final simplification: + // B = x1f<<1 + (1<<(Ï•+2) - fxOneAndAHalf<<1) + const C = 1<<(Ï•+2) - fxOneAndAHalf<<1 + D := x1f<<1 + C // D ranges up to ±1<<(1*Ï•+2). + D <<= Ï• // D ranges up to ±1<<(2*Ï•+2). + D -= x1fSquared // D ranges up to ±1<<(2*Ï•+3). + D *= d // D ranges up to ±1<<(3*Ï•+3). + D /= twoOverS + buf[i] += uint32(D) + } + } + + if i := clamp(x1i, width); i < uint(len(buf)) { + // In ideal math: buf[i] += uint32(d * am) + D := x1fSquared // D ranges up to ±1<<(2*Ï•). + D *= d // D ranges up to ±1<<(3*Ï•). + D /= twoOverS + buf[i] += uint32(D) + } + } + + x = xNext + } +} + +func fixedAccumulateOpOver(dst []uint8, src []uint32) { + // Sanity check that len(dst) >= len(src). + if len(dst) < len(src) { + return + } + + acc := int2Ï•(0) + for i, v := range src { + acc += int2Ï•(v) + a := acc + if a < 0 { + a = -a + } + a >>= 2*Ï• - 16 + if a > 0xffff { + a = 0xffff + } + // This algorithm comes from the standard library's image/draw package. + dstA := uint32(dst[i]) * 0x101 + maskA := uint32(a) + outA := dstA*(0xffff-maskA)/0xffff + maskA + dst[i] = uint8(outA >> 8) + } +} + +func fixedAccumulateOpSrc(dst []uint8, src []uint32) { + // Sanity check that len(dst) >= len(src). + if len(dst) < len(src) { + return + } + + acc := int2Ï•(0) + for i, v := range src { + acc += int2Ï•(v) + a := acc + if a < 0 { + a = -a + } + a >>= 2*Ï• - 8 + if a > 0xff { + a = 0xff + } + dst[i] = uint8(a) + } +} + +func fixedAccumulateMask(buf []uint32) { + acc := int2Ï•(0) + for i, v := range buf { + acc += int2Ï•(v) + a := acc + if a < 0 { + a = -a + } + a >>= 2*Ï• - 16 + if a > 0xffff { + a = 0xffff + } + buf[i] = uint32(a) + } +} diff --git a/vendor/golang.org/x/image/vector/raster_floating.go b/vendor/golang.org/x/image/vector/raster_floating.go new file mode 100644 index 0000000000000..fd11db1b4a158 --- /dev/null +++ b/vendor/golang.org/x/image/vector/raster_floating.go @@ -0,0 +1,220 @@ +// 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 vector + +// This file contains a floating point math implementation of the vector +// graphics rasterizer. + +import ( + "math" +) + +func floatingMax(x, y float32) float32 { + if x > y { + return x + } + return y +} + +func floatingMin(x, y float32) float32 { + if x < y { + return x + } + return y +} + +func floatingFloor(x float32) int32 { return int32(math.Floor(float64(x))) } +func floatingCeil(x float32) int32 { return int32(math.Ceil(float64(x))) } + +func (z *Rasterizer) floatingLineTo(bx, by float32) { + ax, ay := z.penX, z.penY + z.penX, z.penY = bx, by + dir := float32(1) + if ay > by { + dir, ax, ay, bx, by = -1, bx, by, ax, ay + } + // Horizontal line segments yield no change in coverage. Almost horizontal + // segments would yield some change, in ideal math, but the computation + // further below, involving 1 / (by - ay), is unstable in floating point + // math, so we treat the segment as if it was perfectly horizontal. + if by-ay <= 0.000001 { + return + } + dxdy := (bx - ax) / (by - ay) + + x := ax + y := floatingFloor(ay) + yMax := floatingCeil(by) + if yMax > int32(z.size.Y) { + yMax = int32(z.size.Y) + } + width := int32(z.size.X) + + for ; y < yMax; y++ { + dy := floatingMin(float32(y+1), by) - floatingMax(float32(y), ay) + + // The "float32" in expressions like "float32(foo*bar)" here and below + // look redundant, since foo and bar already have type float32, but are + // explicit in order to disable the compiler's Fused Multiply Add (FMA) + // instruction selection, which can improve performance but can result + // in different rounding errors in floating point computations. + // + // This package aims to have bit-exact identical results across all + // GOARCHes, and across pure Go code and assembly, so it disables FMA. + // + // See the discussion at + // https://groups.google.com/d/topic/golang-dev/Sti0bl2xUXQ/discussion + xNext := x + float32(dy*dxdy) + if y < 0 { + x = xNext + continue + } + buf := z.bufF32[y*width:] + d := float32(dy * dir) + x0, x1 := x, xNext + if x > xNext { + x0, x1 = x1, x0 + } + x0i := floatingFloor(x0) + x0Floor := float32(x0i) + x1i := floatingCeil(x1) + x1Ceil := float32(x1i) + + if x1i <= x0i+1 { + xmf := float32(0.5*(x+xNext)) - x0Floor + if i := clamp(x0i+0, width); i < uint(len(buf)) { + buf[i] += d - float32(d*xmf) + } + if i := clamp(x0i+1, width); i < uint(len(buf)) { + buf[i] += float32(d * xmf) + } + } else { + s := 1 / (x1 - x0) + x0f := x0 - x0Floor + oneMinusX0f := 1 - x0f + a0 := float32(0.5 * s * oneMinusX0f * oneMinusX0f) + x1f := x1 - x1Ceil + 1 + am := float32(0.5 * s * x1f * x1f) + + if i := clamp(x0i, width); i < uint(len(buf)) { + buf[i] += float32(d * a0) + } + + if x1i == x0i+2 { + if i := clamp(x0i+1, width); i < uint(len(buf)) { + buf[i] += float32(d * (1 - a0 - am)) + } + } else { + a1 := float32(s * (1.5 - x0f)) + if i := clamp(x0i+1, width); i < uint(len(buf)) { + buf[i] += float32(d * (a1 - a0)) + } + dTimesS := float32(d * s) + for xi := x0i + 2; xi < x1i-1; xi++ { + if i := clamp(xi, width); i < uint(len(buf)) { + buf[i] += dTimesS + } + } + a2 := a1 + float32(s*float32(x1i-x0i-3)) + if i := clamp(x1i-1, width); i < uint(len(buf)) { + buf[i] += float32(d * (1 - a2 - am)) + } + } + + if i := clamp(x1i, width); i < uint(len(buf)) { + buf[i] += float32(d * am) + } + } + + x = xNext + } +} + +const ( + // almost256 scales a floating point value in the range [0, 1] to a uint8 + // value in the range [0x00, 0xff]. + // + // 255 is too small. Floating point math accumulates rounding errors, so a + // fully covered src value that would in ideal math be float32(1) might be + // float32(1-ε), and uint8(255 * (1-ε)) would be 0xfe instead of 0xff. The + // uint8 conversion rounds to zero, not to nearest. + // + // 256 is too big. If we multiplied by 256, below, then a fully covered src + // value of float32(1) would translate to uint8(256 * 1), which can be 0x00 + // instead of the maximal value 0xff. + // + // math.Float32bits(almost256) is 0x437fffff. + almost256 = 255.99998 + + // almost65536 scales a floating point value in the range [0, 1] to a + // uint16 value in the range [0x0000, 0xffff]. + // + // math.Float32bits(almost65536) is 0x477fffff. + almost65536 = almost256 * 256 +) + +func floatingAccumulateOpOver(dst []uint8, src []float32) { + // Sanity check that len(dst) >= len(src). + if len(dst) < len(src) { + return + } + + acc := float32(0) + for i, v := range src { + acc += v + a := acc + if a < 0 { + a = -a + } + if a > 1 { + a = 1 + } + // This algorithm comes from the standard library's image/draw package. + dstA := uint32(dst[i]) * 0x101 + maskA := uint32(almost65536 * a) + outA := dstA*(0xffff-maskA)/0xffff + maskA + dst[i] = uint8(outA >> 8) + } +} + +func floatingAccumulateOpSrc(dst []uint8, src []float32) { + // Sanity check that len(dst) >= len(src). + if len(dst) < len(src) { + return + } + + acc := float32(0) + for i, v := range src { + acc += v + a := acc + if a < 0 { + a = -a + } + if a > 1 { + a = 1 + } + dst[i] = uint8(almost256 * a) + } +} + +func floatingAccumulateMask(dst []uint32, src []float32) { + // Sanity check that len(dst) >= len(src). + if len(dst) < len(src) { + return + } + + acc := float32(0) + for i, v := range src { + acc += v + a := acc + if a < 0 { + a = -a + } + if a > 1 { + a = 1 + } + dst[i] = uint32(almost65536 * a) + } +} diff --git a/vendor/golang.org/x/image/vector/vector.go b/vendor/golang.org/x/image/vector/vector.go new file mode 100644 index 0000000000000..7b8ca987fc45d --- /dev/null +++ b/vendor/golang.org/x/image/vector/vector.go @@ -0,0 +1,472 @@ +// 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 +//go:generate asmfmt -w acc_amd64.s + +// asmfmt is https://github.com/klauspost/asmfmt + +// Package vector provides a rasterizer for 2-D vector graphics. +package vector // import "golang.org/x/image/vector" + +// The rasterizer's design follows +// https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445 +// +// Proof of concept code is in +// https://github.com/google/font-go +// +// See also: +// http://nothings.org/gamedev/rasterize/ +// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm +// https://people.gnome.org/~mathieu/libart/internals.html#INTERNALS-SCANLINE + +import ( + "image" + "image/color" + "image/draw" + "math" +) + +// floatingPointMathThreshold is the width or height above which the rasterizer +// chooses to used floating point math instead of fixed point math. +// +// Both implementations of line segmentation rasterization (see raster_fixed.go +// and raster_floating.go) implement the same algorithm (in ideal, infinite +// precision math) but they perform differently in practice. The fixed point +// math version is roughtly 1.25x faster (on GOARCH=amd64) on the benchmarks, +// but at sufficiently large scales, the computations will overflow and hence +// show rendering artifacts. The floating point math version has more +// consistent quality over larger scales, but it is significantly slower. +// +// This constant determines when to use the faster implementation and when to +// use the better quality implementation. +// +// The rationale for this particular value is that TestRasterizePolygon in +// vector_test.go checks the rendering quality of polygon edges at various +// angles, inscribed in a circle of diameter 512. It may be that a higher value +// would still produce acceptable quality, but 512 seems to work. +const floatingPointMathThreshold = 512 + +func lerp(t, px, py, qx, qy float32) (x, y float32) { + return px + t*(qx-px), py + t*(qy-py) +} + +func clamp(i, width int32) uint { + if i < 0 { + return 0 + } + if i < width { + return uint(i) + } + return uint(width) +} + +// NewRasterizer returns a new Rasterizer whose rendered mask image is bounded +// by the given width and height. +func NewRasterizer(w, h int) *Rasterizer { + z := &Rasterizer{} + z.Reset(w, h) + return z +} + +// Raster is a 2-D vector graphics rasterizer. +// +// The zero value is usable, in that it is a Rasterizer whose rendered mask +// image has zero width and zero height. Call Reset to change its bounds. +type Rasterizer struct { + // bufXxx are buffers of float32 or uint32 values, holding either the + // individual or cumulative area values. + // + // We don't actually need both values at any given time, and to conserve + // memory, the integration of the individual to the cumulative could modify + // the buffer in place. In other words, we could use a single buffer, say + // of type []uint32, and add some math.Float32bits and math.Float32frombits + // calls to satisfy the compiler's type checking. As of Go 1.7, though, + // there is a performance penalty between: + // bufF32[i] += x + // and + // bufU32[i] = math.Float32bits(x + math.Float32frombits(bufU32[i])) + // + // See golang.org/issue/17220 for some discussion. + bufF32 []float32 + bufU32 []uint32 + + useFloatingPointMath bool + + size image.Point + firstX float32 + firstY float32 + penX float32 + penY float32 + + // DrawOp is the operator used for the Draw method. + // + // The zero value is draw.Over. + DrawOp draw.Op + + // TODO: an exported field equivalent to the mask point in the + // draw.DrawMask function in the stdlib image/draw package? +} + +// Reset resets a Rasterizer as if it was just returned by NewRasterizer. +// +// This includes setting z.DrawOp to draw.Over. +func (z *Rasterizer) Reset(w, h int) { + z.size = image.Point{w, h} + z.firstX = 0 + z.firstY = 0 + z.penX = 0 + z.penY = 0 + z.DrawOp = draw.Over + + z.setUseFloatingPointMath(w > floatingPointMathThreshold || h > floatingPointMathThreshold) +} + +func (z *Rasterizer) setUseFloatingPointMath(b bool) { + z.useFloatingPointMath = b + + // Make z.bufF32 or z.bufU32 large enough to hold width * height samples. + if z.useFloatingPointMath { + if n := z.size.X * z.size.Y; n > cap(z.bufF32) { + z.bufF32 = make([]float32, n) + } else { + z.bufF32 = z.bufF32[:n] + for i := range z.bufF32 { + z.bufF32[i] = 0 + } + } + } else { + if n := z.size.X * z.size.Y; n > cap(z.bufU32) { + z.bufU32 = make([]uint32, n) + } else { + z.bufU32 = z.bufU32[:n] + for i := range z.bufU32 { + z.bufU32[i] = 0 + } + } + } +} + +// Size returns the width and height passed to NewRasterizer or Reset. +func (z *Rasterizer) Size() image.Point { + return z.size +} + +// Bounds returns the rectangle from (0, 0) to the width and height passed to +// NewRasterizer or Reset. +func (z *Rasterizer) Bounds() image.Rectangle { + return image.Rectangle{Max: z.size} +} + +// Pen returns the location of the path-drawing pen: the last argument to the +// most recent XxxTo call. +func (z *Rasterizer) Pen() (x, y float32) { + return z.penX, z.penY +} + +// ClosePath closes the current path. +func (z *Rasterizer) ClosePath() { + z.LineTo(z.firstX, z.firstY) +} + +// MoveTo starts a new path and moves the pen to (ax, ay). +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) MoveTo(ax, ay float32) { + z.firstX = ax + z.firstY = ay + z.penX = ax + z.penY = ay +} + +// LineTo adds a line segment, from the pen to (bx, by), and moves the pen to +// (bx, by). +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) LineTo(bx, by float32) { + if z.useFloatingPointMath { + z.floatingLineTo(bx, by) + } else { + z.fixedLineTo(bx, by) + } +} + +// QuadTo adds a quadratic Bézier segment, from the pen via (bx, by) to (cx, +// cy), and moves the pen to (cx, cy). +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) QuadTo(bx, by, cx, cy float32) { + ax, ay := z.penX, z.penY + devsq := devSquared(ax, ay, bx, by, cx, cy) + if devsq >= 0.333 { + const tol = 3 + n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq)))) + t, nInv := float32(0), 1/float32(n) + for i := 0; i < n-1; i++ { + t += nInv + abx, aby := lerp(t, ax, ay, bx, by) + bcx, bcy := lerp(t, bx, by, cx, cy) + z.LineTo(lerp(t, abx, aby, bcx, bcy)) + } + } + z.LineTo(cx, cy) +} + +// CubeTo adds a cubic Bézier segment, from the pen via (bx, by) and (cx, cy) +// to (dx, dy), and moves the pen to (dx, dy). +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) CubeTo(bx, by, cx, cy, dx, dy float32) { + ax, ay := z.penX, z.penY + devsq := devSquared(ax, ay, bx, by, dx, dy) + if devsqAlt := devSquared(ax, ay, cx, cy, dx, dy); devsq < devsqAlt { + devsq = devsqAlt + } + if devsq >= 0.333 { + const tol = 3 + n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq)))) + t, nInv := float32(0), 1/float32(n) + for i := 0; i < n-1; i++ { + t += nInv + abx, aby := lerp(t, ax, ay, bx, by) + bcx, bcy := lerp(t, bx, by, cx, cy) + cdx, cdy := lerp(t, cx, cy, dx, dy) + abcx, abcy := lerp(t, abx, aby, bcx, bcy) + bcdx, bcdy := lerp(t, bcx, bcy, cdx, cdy) + z.LineTo(lerp(t, abcx, abcy, bcdx, bcdy)) + } + } + z.LineTo(dx, dy) +} + +// devSquared returns a measure of how curvy the sequence (ax, ay) to (bx, by) +// to (cx, cy) is. It determines how many line segments will approximate a +// Bézier curve segment. +// +// http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html +// gives the rationale for this evenly spaced heuristic instead of a recursive +// de Casteljau approach: +// +// The reason for the subdivision by n is that I expect the "flatness" +// computation to be semi-expensive (it's done once rather than on each +// potential subdivision) and also because you'll often get fewer subdivisions. +// Taking a circular arc as a simplifying assumption (ie a spherical cow), +// where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't +// made any horrible mistakes, is expected to be 33% more in the limit. +func devSquared(ax, ay, bx, by, cx, cy float32) float32 { + devx := ax - 2*bx + cx + devy := ay - 2*by + cy + return devx*devx + devy*devy +} + +// Draw implements the Drawer interface from the standard library's image/draw +// package. +// +// The vector paths previously added via the XxxTo calls become the mask for +// drawing src onto dst. +func (z *Rasterizer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { + // TODO: adjust r and sp (and mp?) if src.Bounds() doesn't contain + // r.Add(sp.Sub(r.Min)). + + if src, ok := src.(*image.Uniform); ok { + srcR, srcG, srcB, srcA := src.RGBA() + switch dst := dst.(type) { + case *image.Alpha: + // Fast path for glyph rendering. + if srcA == 0xffff { + if z.DrawOp == draw.Over { + z.rasterizeDstAlphaSrcOpaqueOpOver(dst, r) + } else { + z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r) + } + return + } + case *image.RGBA: + if z.DrawOp == draw.Over { + z.rasterizeDstRGBASrcUniformOpOver(dst, r, srcR, srcG, srcB, srcA) + } else { + z.rasterizeDstRGBASrcUniformOpSrc(dst, r, srcR, srcG, srcB, srcA) + } + return + } + } + + if z.DrawOp == draw.Over { + z.rasterizeOpOver(dst, r, src, sp) + } else { + z.rasterizeOpSrc(dst, r, src, sp) + } +} + +func (z *Rasterizer) accumulateMask() { + if z.useFloatingPointMath { + if n := z.size.X * z.size.Y; n > cap(z.bufU32) { + z.bufU32 = make([]uint32, n) + } else { + z.bufU32 = z.bufU32[:n] + } + if haveAccumulateSIMD { + floatingAccumulateMaskSIMD(z.bufU32, z.bufF32) + } else { + floatingAccumulateMask(z.bufU32, z.bufF32) + } + } else { + if haveAccumulateSIMD { + fixedAccumulateMaskSIMD(z.bufU32) + } else { + fixedAccumulateMask(z.bufU32) + } + } +} + +func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpOver(dst *image.Alpha, r image.Rectangle) { + // TODO: non-zero vs even-odd winding? + if r == dst.Bounds() && r == z.Bounds() { + // We bypass the z.accumulateMask step and convert straight from + // z.bufF32 or z.bufU32 to dst.Pix. + if z.useFloatingPointMath { + if haveAccumulateSIMD { + floatingAccumulateOpOverSIMD(dst.Pix, z.bufF32) + } else { + floatingAccumulateOpOver(dst.Pix, z.bufF32) + } + } else { + if haveAccumulateSIMD { + fixedAccumulateOpOverSIMD(dst.Pix, z.bufU32) + } else { + fixedAccumulateOpOver(dst.Pix, z.bufU32) + } + } + return + } + + z.accumulateMask() + pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):] + for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { + for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { + ma := z.bufU32[y*z.size.X+x] + i := y*dst.Stride + x + + // This formula is like rasterizeOpOver's, simplified for the + // concrete dst type and opaque src assumption. + a := 0xffff - ma + pix[i] = uint8((uint32(pix[i])*0x101*a/0xffff + ma) >> 8) + } + } +} + +func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) { + // TODO: non-zero vs even-odd winding? + if r == dst.Bounds() && r == z.Bounds() { + // We bypass the z.accumulateMask step and convert straight from + // z.bufF32 or z.bufU32 to dst.Pix. + if z.useFloatingPointMath { + if haveAccumulateSIMD { + floatingAccumulateOpSrcSIMD(dst.Pix, z.bufF32) + } else { + floatingAccumulateOpSrc(dst.Pix, z.bufF32) + } + } else { + if haveAccumulateSIMD { + fixedAccumulateOpSrcSIMD(dst.Pix, z.bufU32) + } else { + fixedAccumulateOpSrc(dst.Pix, z.bufU32) + } + } + return + } + + z.accumulateMask() + pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):] + for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { + for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { + ma := z.bufU32[y*z.size.X+x] + + // This formula is like rasterizeOpSrc's, simplified for the + // concrete dst type and opaque src assumption. + pix[y*dst.Stride+x] = uint8(ma >> 8) + } + } +} + +func (z *Rasterizer) rasterizeDstRGBASrcUniformOpOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) { + z.accumulateMask() + pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):] + for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { + for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { + ma := z.bufU32[y*z.size.X+x] + + // This formula is like rasterizeOpOver's, simplified for the + // concrete dst type and uniform src assumption. + a := 0xffff - (sa * ma / 0xffff) + i := y*dst.Stride + 4*x + pix[i+0] = uint8(((uint32(pix[i+0])*0x101*a + sr*ma) / 0xffff) >> 8) + pix[i+1] = uint8(((uint32(pix[i+1])*0x101*a + sg*ma) / 0xffff) >> 8) + pix[i+2] = uint8(((uint32(pix[i+2])*0x101*a + sb*ma) / 0xffff) >> 8) + pix[i+3] = uint8(((uint32(pix[i+3])*0x101*a + sa*ma) / 0xffff) >> 8) + } + } +} + +func (z *Rasterizer) rasterizeDstRGBASrcUniformOpSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) { + z.accumulateMask() + pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):] + for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { + for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { + ma := z.bufU32[y*z.size.X+x] + + // This formula is like rasterizeOpSrc's, simplified for the + // concrete dst type and uniform src assumption. + i := y*dst.Stride + 4*x + pix[i+0] = uint8((sr * ma / 0xffff) >> 8) + pix[i+1] = uint8((sg * ma / 0xffff) >> 8) + pix[i+2] = uint8((sb * ma / 0xffff) >> 8) + pix[i+3] = uint8((sa * ma / 0xffff) >> 8) + } + } +} + +func (z *Rasterizer) rasterizeOpOver(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { + z.accumulateMask() + out := color.RGBA64{} + outc := color.Color(&out) + for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { + for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { + sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA() + ma := z.bufU32[y*z.size.X+x] + + // This algorithm comes from the standard library's image/draw + // package. + dr, dg, db, da := dst.At(r.Min.X+x, r.Min.Y+y).RGBA() + a := 0xffff - (sa * ma / 0xffff) + out.R = uint16((dr*a + sr*ma) / 0xffff) + out.G = uint16((dg*a + sg*ma) / 0xffff) + out.B = uint16((db*a + sb*ma) / 0xffff) + out.A = uint16((da*a + sa*ma) / 0xffff) + + dst.Set(r.Min.X+x, r.Min.Y+y, outc) + } + } +} + +func (z *Rasterizer) rasterizeOpSrc(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { + z.accumulateMask() + out := color.RGBA64{} + outc := color.Color(&out) + for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { + for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { + sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA() + ma := z.bufU32[y*z.size.X+x] + + // This algorithm comes from the standard library's image/draw + // package. + out.R = uint16(sr * ma / 0xffff) + out.G = uint16(sg * ma / 0xffff) + out.B = uint16(sb * ma / 0xffff) + out.A = uint16(sa * ma / 0xffff) + + dst.Set(r.Min.X+x, r.Min.Y+y, outc) + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index b34531f2596f8..a0b5b9bbcce9b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,6 +8,9 @@ code.gitea.io/gitea-vet/checks # code.gitea.io/sdk/gitea v0.14.0 ## explicit code.gitea.io/sdk/gitea +# codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797 +## explicit +codeberg.org/Codeberg/avatars # gitea.com/go-chi/binding v0.0.0-20211013065440-d16dc407c2be ## explicit gitea.com/go-chi/binding @@ -749,6 +752,12 @@ github.com/spf13/jwalterweatherman github.com/spf13/pflag # github.com/spf13/viper v1.7.1 github.com/spf13/viper +# github.com/srwiley/oksvg v0.0.0-20211104221756-aeb4ca2c1505 +## explicit +github.com/srwiley/oksvg +# github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 +## explicit +github.com/srwiley/rasterx # github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf ## explicit github.com/ssor/bom @@ -886,6 +895,10 @@ golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/knownhosts +# golang.org/x/image v0.0.0-20190802002840-cff245a6509b +golang.org/x/image/colornames +golang.org/x/image/math/fixed +golang.org/x/image/vector # golang.org/x/mod v0.4.2 golang.org/x/mod/module golang.org/x/mod/semver From 124e00e925ddbd4a2dae02a94badf6a30206aee6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 18 Nov 2021 04:22:01 +0100 Subject: [PATCH 02/13] split rand-avatar-generators into own packages --- modules/avatar/avatar.go | 33 ++++++++++++--------------- modules/avatar/dice_bear/generate.go | 28 +++++++++++++++++++++++ modules/avatar/identicon/generate.go | 34 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 modules/avatar/dice_bear/generate.go create mode 100644 modules/avatar/identicon/generate.go diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 5411a90796f5c..fe9882e53e7aa 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -8,16 +8,15 @@ import ( "bytes" "fmt" "image" - "image/color/palette" _ "image/gif" // for processing gif images _ "image/jpeg" // for processing jpeg images _ "image/png" // for processing png images + "code.gitea.io/gitea/modules/avatar/dice_bear" + "code.gitea.io/gitea/modules/avatar/identicon" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" - "github.com/issue9/identicon" "github.com/nfnt/resize" "github.com/oliamb/cutter" ) @@ -25,27 +24,23 @@ import ( // AvatarSize returns avatar's size const AvatarSize = 290 +type RandomAvatarGenerator interface { + RandomImage(data []byte) (image.Image, error) + RandomImageSize(size int, data []byte) (image.Image, error) +} + // RandomImageSize generates and returns a random avatar image unique to input data // in custom size (height and width). func RandomImageSize(size int, data []byte) (image.Image, error) { - randExtent := len(palette.WebSafe) - 32 - integer, err := util.RandomInt(int64(randExtent)) - if err != nil { - return nil, fmt.Errorf("util.RandomInt: %v", err) - } - colorIndex := int(integer) - backColorIndex := colorIndex - 1 - if backColorIndex < 0 { - backColorIndex = randExtent - 1 - } + kind := "dice_bear" - // Define size, background, and forecolor - imgMaker, err := identicon.New(size, - palette.WebSafe[backColorIndex], palette.WebSafe[colorIndex:colorIndex+32]...) - if err != nil { - return nil, fmt.Errorf("identicon.New: %v", err) + switch kind { + case "dice_bear": + return dice_bear.RandomImageSize(size, data) + default: // "identicon" + return identicon.RandomImageSize(size, data) } - return imgMaker.Make(data), nil + } // RandomImage generates and returns a random avatar image unique to input data diff --git a/modules/avatar/dice_bear/generate.go b/modules/avatar/dice_bear/generate.go new file mode 100644 index 0000000000000..39970bb0ba7de --- /dev/null +++ b/modules/avatar/dice_bear/generate.go @@ -0,0 +1,28 @@ +package dice_bear + +import ( + "image" + "strings" + + "codeberg.org/Codeberg/avatars" + "github.com/srwiley/oksvg" + "github.com/srwiley/rasterx" +) + +func RandomImageSize(size int, data []byte) (image.Image, error) { + avatar := avatars.MakeAvatar(string(data)) + return svg2image(avatar, size, size) +} + +func svg2image(svg string, width, height int) (image.Image, error) { + icon, err := oksvg.ReadIconStream(strings.NewReader(svg)) + if err != nil { + return nil, err + } + + icon.SetTarget(0, 0, float64(width), float64(height)) + rgba := image.NewRGBA(image.Rect(0, 0, width, height)) + icon.Draw(rasterx.NewDasher(width, height, rasterx.NewScannerGV(width, height, rgba, rgba.Bounds())), 1) + + return rgba, nil +} diff --git a/modules/avatar/identicon/generate.go b/modules/avatar/identicon/generate.go new file mode 100644 index 0000000000000..c7556a375b8db --- /dev/null +++ b/modules/avatar/identicon/generate.go @@ -0,0 +1,34 @@ +package identicon + +import ( + "fmt" + "image" + "image/color/palette" + + "code.gitea.io/gitea/modules/util" + + "github.com/issue9/identicon" +) + +// RandomImageSize generates and returns a random avatar image unique to input data +// in custom size (height and width). +func RandomImageSize(size int, data []byte) (image.Image, error) { + randExtent := len(palette.WebSafe) - 32 + integer, err := util.RandomInt(int64(randExtent)) + if err != nil { + return nil, fmt.Errorf("util.RandomInt: %v", err) + } + colorIndex := int(integer) + backColorIndex := colorIndex - 1 + if backColorIndex < 0 { + backColorIndex = randExtent - 1 + } + + // Define size, background, and forecolor + imgMaker, err := identicon.New(size, + palette.WebSafe[backColorIndex], palette.WebSafe[colorIndex:colorIndex+32]...) + if err != nil { + return nil, fmt.Errorf("identicon.New: %v", err) + } + return imgMaker.Make(data), nil +} From dfd00634a11278074b0b40a26b07a1414a47ef7a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 21 Nov 2021 02:23:48 +0100 Subject: [PATCH 03/13] nit --- cmd/web_https.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/web_https.go b/cmd/web_https.go index b939dcab8a119..b0910ca04000f 100644 --- a/cmd/web_https.go +++ b/cmd/web_https.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "github.com/klauspost/cpuid/v2" ) From 12afae7d25ed91b2ecd0304f124e7abc90508c55 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 21 Nov 2021 02:24:28 +0100 Subject: [PATCH 04/13] make it posible to behave different for users/orgs/repos --- integrations/user_avatar_test.go | 2 +- models/repo_avatar.go | 2 +- models/user_avatar.go | 6 +++++- modules/avatar/avatar.go | 26 +++++++++++++++++--------- modules/avatar/avatar_test.go | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/integrations/user_avatar_test.go b/integrations/user_avatar_test.go index edc3a47314ac9..6ba6bae9fdba1 100644 --- a/integrations/user_avatar_test.go +++ b/integrations/user_avatar_test.go @@ -29,7 +29,7 @@ func TestUserAvatar(t *testing.T) { seed = user2.Name } - img, err := avatar.RandomImage([]byte(seed)) + img, err := avatar.RandomImage(avatar.KindRepo, []byte(seed)) if err != nil { assert.NoError(t, err) return diff --git a/models/repo_avatar.go b/models/repo_avatar.go index 6c83e11a5390a..3a3cde386de2c 100644 --- a/models/repo_avatar.go +++ b/models/repo_avatar.go @@ -31,7 +31,7 @@ func (repo *Repository) generateRandomAvatar(e db.Engine) error { idToString := fmt.Sprintf("%d", repo.ID) seed := idToString - img, err := avatar.RandomImage([]byte(seed)) + img, err := avatar.RandomImage(avatar.KindRepo, []byte(seed)) if err != nil { return fmt.Errorf("RandomImage: %v", err) } diff --git a/models/user_avatar.go b/models/user_avatar.go index f96f66f930be4..105caffb22067 100644 --- a/models/user_avatar.go +++ b/models/user_avatar.go @@ -35,7 +35,11 @@ func (u *User) generateRandomAvatar(e db.Engine) error { seed = u.Name } - img, err := avatar.RandomImage([]byte(seed)) + avatarKind := avatar.KindUser + if u.IsOrganization() { + avatarKind = avatar.KindOrg + } + img, err := avatar.RandomImage(avatarKind, []byte(seed)) if err != nil { return fmt.Errorf("RandomImage: %v", err) } diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 47735f88eb56e..9c8d5a649c237 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -24,17 +24,25 @@ import ( // AvatarSize returns avatar's size const AvatarSize = 290 -type RandomAvatarGenerator interface { - RandomImage(data []byte) (image.Image, error) - RandomImageSize(size int, data []byte) (image.Image, error) -} +// Kind represent the type an avatar will be generated for +type Kind uint + +const ( + // User represent users + KindUser Kind = 0 + // Repo represent repositorys + KindRepo Kind = 1 + // Org represent organisations + KindOrg Kind = 2 +) // RandomImageSize generates and returns a random avatar image unique to input data // in custom size (height and width). -func RandomImageSize(size int, data []byte) (image.Image, error) { - kind := "dice_bear" +func RandomImageSize(kind Kind, size int, data []byte) (image.Image, error) { + generator := "dice_bear" - switch kind { + // NOTE: If plugins are invented, make avatar generators to plugins + switch generator { case "dice_bear": return dice_bear.RandomImageSize(size, data) default: // "identicon" @@ -44,8 +52,8 @@ func RandomImageSize(size int, data []byte) (image.Image, error) { // RandomImage generates and returns a random avatar image unique to input data // in default size (height and width). -func RandomImage(data []byte) (image.Image, error) { - return RandomImageSize(AvatarSize, data) +func RandomImage(kind Kind, data []byte) (image.Image, error) { + return RandomImageSize(kind, AvatarSize, data) } // Prepare accepts a byte slice as input, validates it contains an image of an diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go index b958a9e23670a..ae9c1b91d7787 100644 --- a/modules/avatar/avatar_test.go +++ b/modules/avatar/avatar_test.go @@ -22,7 +22,7 @@ func Test_RandomImageSize(t *testing.T) { } func Test_RandomImage(t *testing.T) { - _, err := RandomImage([]byte("gitea@local")) + _, err := RandomImage(KindUser, []byte("gitea@local")) assert.NoError(t, err) } From 414387d117d9cd240ec1b17a4b5e92b3ec33ae47 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 00:40:48 +0200 Subject: [PATCH 05/13] tag dicebear --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dad41d1367b77..49371f54c8b37 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b code.gitea.io/sdk/gitea v0.15.1 - codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797 + codeberg.org/Codeberg/avatars v1.0.0 gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 diff --git a/go.sum b/go.sum index f6f588aef2877..22cf2e6ebf4c9 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b/go.mod h1:zcNbT/aJE code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= -codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797 h1:B1Wf52i7Hd5jiNVXIHdk651FL1E2Zy1GNkkNpvvfS4A= -codeberg.org/Codeberg/avatars v0.0.0-20210202020214-c86887927797/go.mod h1:ML/htpPRb3+owhkm4+qG2ZrXnk5WXaQLASOZ5GLCPi8= +codeberg.org/Codeberg/avatars v1.0.0 h1:MRx5QxuT/oVCcPvC5rXwgwWKD7hc6J0GnZ0Kl67lYEM= +codeberg.org/Codeberg/avatars v1.0.0/go.mod h1:ML/htpPRb3+owhkm4+qG2ZrXnk5WXaQLASOZ5GLCPi8= contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= From aec10e130081ff02075133be1a57d70bfa18b6f8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 01:02:40 +0200 Subject: [PATCH 06/13] nit --- integrations/user_avatar_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/user_avatar_test.go b/integrations/user_avatar_test.go index 30ecf924bba9d..02b97532d60e1 100644 --- a/integrations/user_avatar_test.go +++ b/integrations/user_avatar_test.go @@ -22,14 +22,14 @@ import ( func TestUserAvatar(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { - user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // owner of the repo3, is an org + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // normal user seed := user2.Email if len(seed) == 0 { seed = user2.Name } - img, err := avatar.RandomImage(avatar.KindRepo, []byte(seed)) + img, err := avatar.RandomImage(avatar.KindUser, []byte(seed)) if err != nil { assert.NoError(t, err) return From 951fba99e2b7e22409a38ceeb536eab763a43fa5 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 01:54:01 +0200 Subject: [PATCH 07/13] infent fancy interfaces --- custom/conf/app.example.ini | 14 +++- models/organization/org.go | 1 + models/repo/avatar.go | 13 ++-- models/user/avatar.go | 7 +- modules/avatar/avatar.go | 74 ++++++++++++++++++---- modules/avatar/avatar_test.go | 4 +- modules/avatar/dice_bear/generate.go | 22 ++++++- modules/avatar/identicon/generate.go | 29 ++++++++- modules/avatar/identicon/identicon.go | 14 ++-- modules/avatar/identicon/identicon_test.go | 2 +- modules/avatar/none/none.go | 28 ++++++++ 11 files changed, 177 insertions(+), 31 deletions(-) create mode 100644 modules/avatar/none/none.go diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 08708948940fa..8e6a105078a4a 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1639,8 +1639,20 @@ ROUTER = console ;AVATAR_UPLOAD_PATH = data/avatars ;REPOSITORY_AVATAR_UPLOAD_PATH = data/repo-avatars ;; +;; How Gitea deals with missing user avatars +;; * random = random avatar will be displayed +;; * image = default image will be used +;USER_AVATAR_FALLBACK = random +;; +;; How Gitea deals with missing organization avatars +;; * random = random avatar will be displayed; +;; * image = default image will be used +;ORGANIZATION_AVATAR_FALLBACK = random +;; ;; How Gitea deals with missing repository avatars -;; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used +;; * none = no avatar will be displayed +;; * random = random avatar will be displayed +;; * image = default image will be used ;REPOSITORY_AVATAR_FALLBACK = none ;REPOSITORY_AVATAR_FALLBACK_IMAGE = /img/repo_default.png ;; diff --git a/models/organization/org.go b/models/organization/org.go index 044ea065637c5..c0c47733dbcb7 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -281,6 +281,7 @@ func CreateOrganization(org *Organization, owner *user_model.User) (err error) { if err = db.Insert(ctx, org); err != nil { return fmt.Errorf("insert organization: %v", err) } + if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil { return fmt.Errorf("generate random avatar: %v", err) } diff --git a/models/repo/avatar.go b/models/repo/avatar.go index 68dcd0ced8b24..7d43560c9542b 100644 --- a/models/repo/avatar.go +++ b/models/repo/avatar.go @@ -39,6 +39,11 @@ func generateRandomAvatar(ctx context.Context, repo *Repository) error { return fmt.Errorf("RandomImage: %v", err) } + if img == nil { + // use default repo image + return nil + } + repo.Avatar = idToString if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error { @@ -66,13 +71,13 @@ func (repo *Repository) relAvatarLink(ctx context.Context) string { switch mode := setting.RepoAvatar.Fallback; mode { case "image": return setting.RepoAvatar.FallbackImage - case "random": + case "none": + // default behaviour: do not display avatar + return "" + default: if err := generateRandomAvatar(ctx, repo); err != nil { log.Error("generateRandomAvatar: %v", err) } - default: - // default behaviour: do not display avatar - return "" } } return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar) diff --git a/models/user/avatar.go b/models/user/avatar.go index 313e8af52a8d0..bcea720ef4f04 100644 --- a/models/user/avatar.go +++ b/models/user/avatar.go @@ -41,6 +41,11 @@ func GenerateRandomAvatar(ctx context.Context, u *User) error { return fmt.Errorf("RandomImage: %v", err) } + if img == nil { + // use default user image + return nil + } + u.Avatar = avatars.HashEmail(seed) // Don't share the images so that we can delete them easily @@ -57,7 +62,7 @@ func GenerateRandomAvatar(ctx context.Context, u *User) error { return err } - log.Info("New random avatar created: %d", u.ID) + log.Info("New random avatar for user[%d] created", u.ID) return nil } diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 9c8d5a649c237..df7a47120aad0 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -1,3 +1,4 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -15,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/avatar/dice_bear" "code.gitea.io/gitea/modules/avatar/identicon" + "code.gitea.io/gitea/modules/avatar/none" "code.gitea.io/gitea/modules/setting" "github.com/nfnt/resize" @@ -36,24 +38,74 @@ const ( KindOrg Kind = 2 ) +type randomImageGenerator interface { + Name() string +} + +type randomUserImageGenerator interface { + randomImageGenerator + RandomUserImage(int, []byte) (image.Image, error) +} + +type randomOrgImageGenerator interface { + randomImageGenerator + RandomOrgImage(int, []byte) (image.Image, error) +} + +type randomRepoImageGenerator interface { + randomImageGenerator + RandomRepoImage(int, []byte) (image.Image, error) +} + +var ( + userImageGenerator randomUserImageGenerator = identicon.Identicon{} + orgImageGenerator randomOrgImageGenerator = identicon.Identicon{} + repoImageGenerator randomRepoImageGenerator = identicon.Identicon{} + generators = []randomImageGenerator{ + dice_bear.DiceBear{}, + identicon.Identicon{}, + none.None{}, + } +) + +// TODO: Init() +func init() { + userPreference := "none" + orgPreference := "none" + repoPreference := setting.RepoAvatar.FallbackImage + + for _, g := range generators { + if g, ok := g.(randomUserImageGenerator); ok && userPreference == g.Name() { + userImageGenerator = g + } + if g, ok := g.(randomOrgImageGenerator); ok && orgPreference == g.Name() { + orgImageGenerator = g + } + if g, ok := g.(randomRepoImageGenerator); ok && repoPreference == g.Name() { + repoImageGenerator = g + } + } +} + // RandomImageSize generates and returns a random avatar image unique to input data // in custom size (height and width). -func RandomImageSize(kind Kind, size int, data []byte) (image.Image, error) { - generator := "dice_bear" - - // NOTE: If plugins are invented, make avatar generators to plugins - switch generator { - case "dice_bear": - return dice_bear.RandomImageSize(size, data) - default: // "identicon" - return identicon.RandomImageSize(size, data) +func RandomImageSize(kind Kind, size int, seed []byte) (image.Image, error) { + switch kind { + case KindUser: + return userImageGenerator.RandomUserImage(size, seed) + case KindOrg: + return orgImageGenerator.RandomOrgImage(size, seed) + case KindRepo: + return repoImageGenerator.RandomRepoImage(size, seed) + default: + return nil, fmt.Errorf("avatar kind %v not supported", kind) } } // RandomImage generates and returns a random avatar image unique to input data // in default size (height and width). -func RandomImage(kind Kind, data []byte) (image.Image, error) { - return RandomImageSize(kind, AvatarSize, data) +func RandomImage(kind Kind, seed []byte) (image.Image, error) { + return RandomImageSize(kind, AvatarSize, seed) } // Prepare accepts a byte slice as input, validates it contains an image of an diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go index 4c06fd2cc714a..d1dfa4a94c4e7 100644 --- a/modules/avatar/avatar_test.go +++ b/modules/avatar/avatar_test.go @@ -14,10 +14,10 @@ import ( ) func Test_RandomImageSize(t *testing.T) { - _, err := RandomImageSize(0, []byte("gitea@local")) + _, err := RandomImageSize(KindOrg, 0, []byte("gitea@local")) assert.Error(t, err) - _, err = RandomImageSize(64, []byte("gitea@local")) + _, err = RandomImageSize(KindRepo, 64, []byte("gitea@local")) assert.NoError(t, err) } diff --git a/modules/avatar/dice_bear/generate.go b/modules/avatar/dice_bear/generate.go index 39970bb0ba7de..132f7cefa7630 100644 --- a/modules/avatar/dice_bear/generate.go +++ b/modules/avatar/dice_bear/generate.go @@ -1,3 +1,7 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package dice_bear import ( @@ -9,7 +13,23 @@ import ( "github.com/srwiley/rasterx" ) -func RandomImageSize(size int, data []byte) (image.Image, error) { +// Identicon is used to generate pseudo-random avatars +type DiceBear struct{} + +func (_ DiceBear) Name() string { + return "dicebear" +} + +func (_ DiceBear) RandomUserImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (_ DiceBear) RandomOrgImage(size int, data []byte) (image.Image, error) { + // TODO: group 4 images to one + return randomImageSize(size, data) +} + +func randomImageSize(size int, data []byte) (image.Image, error) { avatar := avatars.MakeAvatar(string(data)) return svg2image(avatar, size, size) } diff --git a/modules/avatar/identicon/generate.go b/modules/avatar/identicon/generate.go index a6ce367b4744c..f2265fe0524dc 100644 --- a/modules/avatar/identicon/generate.go +++ b/modules/avatar/identicon/generate.go @@ -1,3 +1,7 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package identicon import ( @@ -6,11 +10,30 @@ import ( "image/color" ) -// RandomImageSize generates and returns a random avatar image unique to input data +// Identicon is used to generate pseudo-random avatars +type Identicon struct{} + +func (_ Identicon) Name() string { + return "identicon" +} + +func (_ Identicon) RandomUserImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (_ Identicon) RandomOrgImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (_ Identicon) RandomRepoImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +// randomImageSize generates and returns a random avatar image unique to input data // in custom size (height and width). -func RandomImageSize(size int, data []byte) (image.Image, error) { +func randomImageSize(size int, data []byte) (image.Image, error) { // we use white as background, and use dark colors to draw blocks - imgMaker, err := New(size, color.White, DarkColors...) + imgMaker, err := new(size, color.White, DarkColors...) if err != nil { return nil, fmt.Errorf("identicon.New: %v", err) } diff --git a/modules/avatar/identicon/identicon.go b/modules/avatar/identicon/identicon.go index cc7e2a791d018..509e49e954c85 100644 --- a/modules/avatar/identicon/identicon.go +++ b/modules/avatar/identicon/identicon.go @@ -16,19 +16,19 @@ import ( const minImageSize = 16 -// Identicon is used to generate pseudo-random avatars -type Identicon struct { +// identicon is used to generate pseudo-random avatars +type identicon struct { foreColors []color.Color backColor color.Color size int rect image.Rectangle } -// New returns an Identicon struct with the correct settings +// new returns an Identicon struct with the correct settings // size image size // back background color // fore all possible foreground colors. only one foreground color will be picked randomly for one image -func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) { +func new(size int, back color.Color, fore ...color.Color) (*identicon, error) { if len(fore) == 0 { return nil, fmt.Errorf("foreground is not set") } @@ -37,7 +37,7 @@ func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) { return nil, fmt.Errorf("size %d is smaller than min size %d", size, minImageSize) } - return &Identicon{ + return &identicon{ foreColors: fore, backColor: back, size: size, @@ -46,7 +46,7 @@ func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) { } // Make generates an avatar by data -func (i *Identicon) Make(data []byte) image.Image { +func (i *identicon) Make(data []byte) image.Image { h := sha256.New() h.Write(data) sum := h.Sum(nil) @@ -61,7 +61,7 @@ func (i *Identicon) Make(data []byte) image.Image { return i.render(c, b1, b2, b1Angle, b2Angle, foreColor) } -func (i *Identicon) render(c, b1, b2, b1Angle, b2Angle, foreColor int) image.Image { +func (i *identicon) render(c, b1, b2, b1Angle, b2Angle, foreColor int) image.Image { p := image.NewPaletted(i.rect, []color.Color{i.backColor, i.foreColors[foreColor]}) drawBlocks(p, i.size, centerBlocks[c], blocks[b1], blocks[b2], b1Angle, b2Angle) return p diff --git a/modules/avatar/identicon/identicon_test.go b/modules/avatar/identicon/identicon_test.go index 44635fbb3bd50..62e069e3e83b4 100644 --- a/modules/avatar/identicon/identicon_test.go +++ b/modules/avatar/identicon/identicon_test.go @@ -24,7 +24,7 @@ func TestGenerate(t *testing.T) { } backColor := color.White - imgMaker, err := New(64, backColor, DarkColors...) + imgMaker, err := new(64, backColor, DarkColors...) assert.NoError(t, err) for i := 0; i < 100; i++ { s := strconv.Itoa(i) diff --git a/modules/avatar/none/none.go b/modules/avatar/none/none.go new file mode 100644 index 0000000000000..98a17a0e12137 --- /dev/null +++ b/modules/avatar/none/none.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package none + +import ( + "image" +) + +// None wont generate an image +type None struct{} + +func (_ None) Name() string { + return "none" +} + +func (_ None) RandomUserImage(size int, data []byte) (image.Image, error) { + return nil, nil +} + +func (_ None) RandomOrgImage(size int, data []byte) (image.Image, error) { + return nil, nil +} + +func (_ None) RandomRepoImage(size int, data []byte) (image.Image, error) { + return nil, nil +} From dda44fd217548c83bb58b5bf81a8caadc4aaf21f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 04:15:25 +0200 Subject: [PATCH 08/13] add robot --- go.mod | 4 +++ go.sum | 8 +++++ modules/avatar/avatar.go | 2 ++ modules/avatar/dice_bear/generate.go | 20 ++++++++++-- modules/avatar/robot/generate.go | 47 ++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 modules/avatar/robot/generate.go diff --git a/go.mod b/go.mod index 49371f54c8b37..6022a2c149832 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 github.com/klauspost/compress v1.15.3 github.com/klauspost/cpuid/v2 v2.0.12 + github.com/lafriks/go-avatars v0.3.0 github.com/lib/pq v1.10.5 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/markbates/goth v1.72.0 @@ -162,6 +163,7 @@ require ( github.com/envoyproxy/go-control-plane v0.10.1 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/fogleman/gg v1.3.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fullstorydev/grpcurl v1.8.1 // indirect @@ -186,6 +188,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -215,6 +218,7 @@ require ( github.com/klauspost/pgzip v1.2.5 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect + github.com/lafriks/go-svg v0.3.2 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index 22cf2e6ebf4c9..1e79eaf24812d 100644 --- a/go.sum +++ b/go.sum @@ -436,6 +436,8 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -684,6 +686,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1066,6 +1070,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/go-gypsy v1.0.0/go.mod h1:chkXM0zjdpXOiqkCW1XcCHDfjfk14PH2KKkQWxfJUcU= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lafriks/go-avatars v0.3.0 h1:+azteSFi6cr8TlvglfaBuwueeBwF8n84YYpKW0EWVEU= +github.com/lafriks/go-avatars v0.3.0/go.mod h1:EW0FFXaF0VZo4/vRoh1ASxqwKizRWfkbfrC21gEwxJY= +github.com/lafriks/go-svg v0.3.2 h1:wuSgV5Jh+aSGe+zPwXIOo2qb3UBRMKtAQmGfWwgIPqM= +github.com/lafriks/go-svg v0.3.2/go.mod h1:Gh57dXusEHiJBarKTI+t+LJVLU3Y7EvU/OUHWQUIBV0= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index df7a47120aad0..013b58328a8c6 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/avatar/dice_bear" "code.gitea.io/gitea/modules/avatar/identicon" "code.gitea.io/gitea/modules/avatar/none" + "code.gitea.io/gitea/modules/avatar/robot" "code.gitea.io/gitea/modules/setting" "github.com/nfnt/resize" @@ -65,6 +66,7 @@ var ( dice_bear.DiceBear{}, identicon.Identicon{}, none.None{}, + robot.Robot{}, } ) diff --git a/modules/avatar/dice_bear/generate.go b/modules/avatar/dice_bear/generate.go index 132f7cefa7630..19eee9d7cbe10 100644 --- a/modules/avatar/dice_bear/generate.go +++ b/modules/avatar/dice_bear/generate.go @@ -5,7 +5,9 @@ package dice_bear import ( + "fmt" "image" + "image/draw" "strings" "codeberg.org/Codeberg/avatars" @@ -13,7 +15,7 @@ import ( "github.com/srwiley/rasterx" ) -// Identicon is used to generate pseudo-random avatars +// DiceBear is used to generate pseudo-random avatars type DiceBear struct{} func (_ DiceBear) Name() string { @@ -25,8 +27,20 @@ func (_ DiceBear) RandomUserImage(size int, data []byte) (image.Image, error) { } func (_ DiceBear) RandomOrgImage(size int, data []byte) (image.Image, error) { - // TODO: group 4 images to one - return randomImageSize(size, data) + size = size / 2 + space := size / 20 + img := image.NewRGBA(image.Rect(0, 0, size*2, size*2)) + + for i := 0; i < 4; i++ { + av, err := randomImageSize(size, []byte(fmt.Sprintf("%s-%d", string(data), i))) + if err != nil { + return nil, err + } + pos := image.Rect((i-int(i/2)*2)*(size+space), int(i/2)*(size+space), ((i-int(i/2)*2)+1)*(size+space), (int(i/2)+1)*(size+space)) + draw.Draw(img, pos, av, image.Point{}, draw.Over) + } + + return img, nil } func randomImageSize(size int, data []byte) (image.Image, error) { diff --git a/modules/avatar/robot/generate.go b/modules/avatar/robot/generate.go new file mode 100644 index 0000000000000..e1c5f0d00c3fc --- /dev/null +++ b/modules/avatar/robot/generate.go @@ -0,0 +1,47 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package robot + +import ( + "fmt" + "image" + "image/draw" + + "github.com/lafriks/go-avatars" +) + +// Robot is used to generate pseudo-random avatars +type Robot struct{} + +func (_ Robot) Name() string { + return "robot" +} + +func (_ Robot) RandomUserImage(size int, data []byte) (image.Image, error) { + a, err := avatars.Generate(string(data)) + if err != nil { + return nil, err + } + return a.Image(avatars.RenderSize(size)) +} + +func (_ Robot) RandomOrgImage(size int, data []byte) (image.Image, error) { + img := image.NewRGBA(image.Rect(0, 0, size*2, size*2)) + + for i := 0; i < 4; i++ { + a, err := avatars.Generate(fmt.Sprintf("%s-%d", string(data), i)) + if err != nil { + return nil, err + } + av, err := a.Image(avatars.RenderSize(size)) + if err != nil { + return nil, err + } + pos := image.Rect((i-int(i/2)*2)*size, int(i/2)*size, ((i-int(i/2)*2)+1)*size, (int(i/2)+1)*size) + draw.Draw(img, pos, av, image.Point{}, draw.Over) + } + + return img, nil +} From 77508788d053af9ea9fa5720f63caf1ceadc9d81 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 04:26:51 +0200 Subject: [PATCH 09/13] use only one lib to convert svg to png --- go.mod | 6 ++---- go.sum | 4 ---- modules/avatar/dice_bear/generate.go | 21 ++++++++++----------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 6022a2c149832..f3f04ead31686 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/emirpasic/gods v1.18.1 github.com/ethantkoenig/rupture v1.0.1 github.com/felixge/fgprof v0.9.2 + github.com/fogleman/gg v1.3.0 github.com/gliderlabs/ssh v0.3.4 github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d @@ -62,6 +63,7 @@ require ( github.com/klauspost/compress v1.15.3 github.com/klauspost/cpuid/v2 v2.0.12 github.com/lafriks/go-avatars v0.3.0 + github.com/lafriks/go-svg v0.3.2 github.com/lib/pq v1.10.5 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/markbates/goth v1.72.0 @@ -82,8 +84,6 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 github.com/sergi/go-diff v1.2.0 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 - github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 - github.com/srwiley/rasterx v0.0.0-20220615024203-67b7089efd25 github.com/stretchr/testify v1.7.1 github.com/syndtr/goleveldb v1.0.0 github.com/tstranex/u2f v1.0.0 @@ -163,7 +163,6 @@ require ( github.com/envoyproxy/go-control-plane v0.10.1 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect - github.com/fogleman/gg v1.3.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fullstorydev/grpcurl v1.8.1 // indirect @@ -218,7 +217,6 @@ require ( github.com/klauspost/pgzip v1.2.5 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect - github.com/lafriks/go-svg v0.3.2 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index 1e79eaf24812d..74b39265d8db4 100644 --- a/go.sum +++ b/go.sum @@ -1463,10 +1463,6 @@ github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= -github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 h1:XPYXKIuH/n5zpUoEWk2jWV/SjEMNYmqDYmTgbjmhtaI= -github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= -github.com/srwiley/rasterx v0.0.0-20220615024203-67b7089efd25 h1:uyi1c8AxAw2b9MTr639iqz45FMFb4dDR7rB++NaVooo= -github.com/srwiley/rasterx v0.0.0-20220615024203-67b7089efd25/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= diff --git a/modules/avatar/dice_bear/generate.go b/modules/avatar/dice_bear/generate.go index 19eee9d7cbe10..6f1965f63d9e6 100644 --- a/modules/avatar/dice_bear/generate.go +++ b/modules/avatar/dice_bear/generate.go @@ -11,8 +11,11 @@ import ( "strings" "codeberg.org/Codeberg/avatars" - "github.com/srwiley/oksvg" - "github.com/srwiley/rasterx" + + "github.com/fogleman/gg" + "github.com/lafriks/go-svg" + "github.com/lafriks/go-svg/renderer" + rendr_gg "github.com/lafriks/go-svg/renderer/gg" ) // DiceBear is used to generate pseudo-random avatars @@ -44,19 +47,15 @@ func (_ DiceBear) RandomOrgImage(size int, data []byte) (image.Image, error) { } func randomImageSize(size int, data []byte) (image.Image, error) { - avatar := avatars.MakeAvatar(string(data)) - return svg2image(avatar, size, size) -} + svgAvatar := avatars.MakeAvatar(string(data)) -func svg2image(svg string, width, height int) (image.Image, error) { - icon, err := oksvg.ReadIconStream(strings.NewReader(svg)) + s, err := svg.Parse(strings.NewReader(svgAvatar), svg.IgnoreErrorMode) if err != nil { return nil, err } - icon.SetTarget(0, 0, float64(width), float64(height)) - rgba := image.NewRGBA(image.Rect(0, 0, width, height)) - icon.Draw(rasterx.NewDasher(width, height, rasterx.NewScannerGV(width, height, rgba, rgba.Bounds())), 1) + gc := gg.NewContext(size, size) + rendr_gg.Draw(gc, s, renderer.Target(0, 0, float64(size), float64(size))) - return rgba, nil + return gc.Image(), nil } From 0a0b147513e5e5f56cc6475ec406ab49b497a0c1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 04:50:21 +0200 Subject: [PATCH 10/13] fix lint --- modules/avatar/avatar.go | 4 ++-- modules/avatar/{dice_bear => dicebear}/generate.go | 13 ++++++------- modules/avatar/identicon/generate.go | 8 ++++---- modules/avatar/none/none.go | 8 ++++---- modules/avatar/robot/generate.go | 9 +++++---- 5 files changed, 21 insertions(+), 21 deletions(-) rename modules/avatar/{dice_bear => dicebear}/generate.go (76%) diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 013b58328a8c6..650730792497e 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -14,7 +14,7 @@ import ( _ "image/jpeg" // for processing jpeg images _ "image/png" // for processing png images - "code.gitea.io/gitea/modules/avatar/dice_bear" + "code.gitea.io/gitea/modules/avatar/dicebear" "code.gitea.io/gitea/modules/avatar/identicon" "code.gitea.io/gitea/modules/avatar/none" "code.gitea.io/gitea/modules/avatar/robot" @@ -63,7 +63,7 @@ var ( orgImageGenerator randomOrgImageGenerator = identicon.Identicon{} repoImageGenerator randomRepoImageGenerator = identicon.Identicon{} generators = []randomImageGenerator{ - dice_bear.DiceBear{}, + dicebear.DiceBear{}, identicon.Identicon{}, none.None{}, robot.Robot{}, diff --git a/modules/avatar/dice_bear/generate.go b/modules/avatar/dicebear/generate.go similarity index 76% rename from modules/avatar/dice_bear/generate.go rename to modules/avatar/dicebear/generate.go index 6f1965f63d9e6..1b6bac9bd4f90 100644 --- a/modules/avatar/dice_bear/generate.go +++ b/modules/avatar/dicebear/generate.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package dice_bear +package dicebear import ( "fmt" @@ -11,7 +11,6 @@ import ( "strings" "codeberg.org/Codeberg/avatars" - "github.com/fogleman/gg" "github.com/lafriks/go-svg" "github.com/lafriks/go-svg/renderer" @@ -21,16 +20,16 @@ import ( // DiceBear is used to generate pseudo-random avatars type DiceBear struct{} -func (_ DiceBear) Name() string { +func (DiceBear) Name() string { return "dicebear" } -func (_ DiceBear) RandomUserImage(size int, data []byte) (image.Image, error) { +func (DiceBear) RandomUserImage(size int, data []byte) (image.Image, error) { return randomImageSize(size, data) } -func (_ DiceBear) RandomOrgImage(size int, data []byte) (image.Image, error) { - size = size / 2 +func (DiceBear) RandomOrgImage(size int, data []byte) (image.Image, error) { + size /= 2 space := size / 20 img := image.NewRGBA(image.Rect(0, 0, size*2, size*2)) @@ -39,7 +38,7 @@ func (_ DiceBear) RandomOrgImage(size int, data []byte) (image.Image, error) { if err != nil { return nil, err } - pos := image.Rect((i-int(i/2)*2)*(size+space), int(i/2)*(size+space), ((i-int(i/2)*2)+1)*(size+space), (int(i/2)+1)*(size+space)) + pos := image.Rect((i-(i/2)*2)*(size+space), (i/2)*(size+space), ((i-(i/2)*2)+1)*(size+space), ((i/2)+1)*(size+space)) draw.Draw(img, pos, av, image.Point{}, draw.Over) } diff --git a/modules/avatar/identicon/generate.go b/modules/avatar/identicon/generate.go index f2265fe0524dc..75dc606086ad6 100644 --- a/modules/avatar/identicon/generate.go +++ b/modules/avatar/identicon/generate.go @@ -13,19 +13,19 @@ import ( // Identicon is used to generate pseudo-random avatars type Identicon struct{} -func (_ Identicon) Name() string { +func (Identicon) Name() string { return "identicon" } -func (_ Identicon) RandomUserImage(size int, data []byte) (image.Image, error) { +func (Identicon) RandomUserImage(size int, data []byte) (image.Image, error) { return randomImageSize(size, data) } -func (_ Identicon) RandomOrgImage(size int, data []byte) (image.Image, error) { +func (Identicon) RandomOrgImage(size int, data []byte) (image.Image, error) { return randomImageSize(size, data) } -func (_ Identicon) RandomRepoImage(size int, data []byte) (image.Image, error) { +func (Identicon) RandomRepoImage(size int, data []byte) (image.Image, error) { return randomImageSize(size, data) } diff --git a/modules/avatar/none/none.go b/modules/avatar/none/none.go index 98a17a0e12137..df31f2add60e0 100644 --- a/modules/avatar/none/none.go +++ b/modules/avatar/none/none.go @@ -11,18 +11,18 @@ import ( // None wont generate an image type None struct{} -func (_ None) Name() string { +func (None) Name() string { return "none" } -func (_ None) RandomUserImage(size int, data []byte) (image.Image, error) { +func (None) RandomUserImage(size int, data []byte) (image.Image, error) { return nil, nil } -func (_ None) RandomOrgImage(size int, data []byte) (image.Image, error) { +func (None) RandomOrgImage(size int, data []byte) (image.Image, error) { return nil, nil } -func (_ None) RandomRepoImage(size int, data []byte) (image.Image, error) { +func (None) RandomRepoImage(size int, data []byte) (image.Image, error) { return nil, nil } diff --git a/modules/avatar/robot/generate.go b/modules/avatar/robot/generate.go index e1c5f0d00c3fc..e4ce23428a1b8 100644 --- a/modules/avatar/robot/generate.go +++ b/modules/avatar/robot/generate.go @@ -15,11 +15,11 @@ import ( // Robot is used to generate pseudo-random avatars type Robot struct{} -func (_ Robot) Name() string { +func (Robot) Name() string { return "robot" } -func (_ Robot) RandomUserImage(size int, data []byte) (image.Image, error) { +func (Robot) RandomUserImage(size int, data []byte) (image.Image, error) { a, err := avatars.Generate(string(data)) if err != nil { return nil, err @@ -27,7 +27,8 @@ func (_ Robot) RandomUserImage(size int, data []byte) (image.Image, error) { return a.Image(avatars.RenderSize(size)) } -func (_ Robot) RandomOrgImage(size int, data []byte) (image.Image, error) { +func (Robot) RandomOrgImage(size int, data []byte) (image.Image, error) { + size /= 2 img := image.NewRGBA(image.Rect(0, 0, size*2, size*2)) for i := 0; i < 4; i++ { @@ -39,7 +40,7 @@ func (_ Robot) RandomOrgImage(size int, data []byte) (image.Image, error) { if err != nil { return nil, err } - pos := image.Rect((i-int(i/2)*2)*size, int(i/2)*size, ((i-int(i/2)*2)+1)*size, (int(i/2)+1)*size) + pos := image.Rect((i-(i/2)*2)*size, (i/2)*size, ((i-(i/2)*2)+1)*size, ((i/2)+1)*size) draw.Draw(img, pos, av, image.Point{}, draw.Over) } From 309c5e1f6e311c9239497c69bb7a8e4428521a4b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 05:16:06 +0200 Subject: [PATCH 11/13] add monsterid --- go.mod | 1 + go.sum | 2 ++ modules/avatar/avatar.go | 2 ++ modules/avatar/monsterid/generate.go | 34 ++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 modules/avatar/monsterid/generate.go diff --git a/go.mod b/go.mod index f3f04ead31686..396371910b740 100644 --- a/go.mod +++ b/go.mod @@ -106,6 +106,7 @@ require ( gopkg.in/ini.v1 v1.66.4 gopkg.in/yaml.v2 v2.4.0 mvdan.cc/xurls/v2 v2.4.0 + src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.11 xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f diff --git a/go.sum b/go.sum index 74b39265d8db4..426405f65cf81 100644 --- a/go.sum +++ b/go.sum @@ -2430,6 +2430,8 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496 h1:c+syTOBF/T6w7vi7KoDpFHQsAxWrZBn2oJfLqylWqgo= +src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496/go.mod h1:C4IzHjWw8+3/osm2FWUib2Ax2owZ1oKx4rzBZilBS8I= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 650730792497e..fb51719829088 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/avatar/dicebear" "code.gitea.io/gitea/modules/avatar/identicon" + "code.gitea.io/gitea/modules/avatar/monsterid" "code.gitea.io/gitea/modules/avatar/none" "code.gitea.io/gitea/modules/avatar/robot" "code.gitea.io/gitea/modules/setting" @@ -67,6 +68,7 @@ var ( identicon.Identicon{}, none.None{}, robot.Robot{}, + monsterid.Monster{}, } ) diff --git a/modules/avatar/monsterid/generate.go b/modules/avatar/monsterid/generate.go new file mode 100644 index 0000000000000..ec21e0110997f --- /dev/null +++ b/modules/avatar/monsterid/generate.go @@ -0,0 +1,34 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package monsterid + +import ( + "image" + + monster "src.techknowlogick.com/monster-id" +) + +// Monster is used to generate pseudo-random avatars +type Monster struct{} + +func (Monster) Name() string { + return "monsterid" +} + +func (Monster) RandomUserImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (Monster) RandomOrgImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (Monster) RandomRepoImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func randomImageSize(size int, data []byte) (image.Image, error) { + return monster.New(data), nil +} From 8c6bed3f1956f9e95c8c81fc4223afb6851b23b9 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 05:21:40 +0200 Subject: [PATCH 12/13] add wavatars --- go.mod | 1 + go.sum | 2 ++ modules/avatar/avatar.go | 2 ++ modules/avatar/wavatars/generate.go | 34 +++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 modules/avatar/wavatars/generate.go diff --git a/go.mod b/go.mod index 396371910b740..281e83ea798c8 100644 --- a/go.mod +++ b/go.mod @@ -107,6 +107,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 mvdan.cc/xurls/v2 v2.4.0 src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496 + src.techknowlogick.com/wavatars v0.0.0-20190220172003-d08f03a6b865 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.11 xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f diff --git a/go.sum b/go.sum index 426405f65cf81..412aa8dbc1f3a 100644 --- a/go.sum +++ b/go.sum @@ -2432,6 +2432,8 @@ sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496 h1:c+syTOBF/T6w7vi7KoDpFHQsAxWrZBn2oJfLqylWqgo= src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496/go.mod h1:C4IzHjWw8+3/osm2FWUib2Ax2owZ1oKx4rzBZilBS8I= +src.techknowlogick.com/wavatars v0.0.0-20190220172003-d08f03a6b865 h1:X2c3+d3MscxYD2FxBGAYNGmLA0SVtDRWmDFmLADL2Wc= +src.techknowlogick.com/wavatars v0.0.0-20190220172003-d08f03a6b865/go.mod h1:F3KS3mPE6x39Llpd05OjyZnVoQl70UomA7ntxg/jNCQ= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index fb51719829088..c48bdefd1b428 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/avatar/monsterid" "code.gitea.io/gitea/modules/avatar/none" "code.gitea.io/gitea/modules/avatar/robot" + "code.gitea.io/gitea/modules/avatar/wavatars" "code.gitea.io/gitea/modules/setting" "github.com/nfnt/resize" @@ -69,6 +70,7 @@ var ( none.None{}, robot.Robot{}, monsterid.Monster{}, + wavatars.Wavatars{}, } ) diff --git a/modules/avatar/wavatars/generate.go b/modules/avatar/wavatars/generate.go new file mode 100644 index 0000000000000..ee3015d364eaf --- /dev/null +++ b/modules/avatar/wavatars/generate.go @@ -0,0 +1,34 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package wavatars + +import ( + "image" + + "src.techknowlogick.com/wavatars" +) + +// Wavatars is used to generate pseudo-random avatars +type Wavatars struct{} + +func (Wavatars) Name() string { + return "wavatars" +} + +func (Wavatars) RandomUserImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (Wavatars) RandomOrgImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func (Wavatars) RandomRepoImage(size int, data []byte) (image.Image, error) { + return randomImageSize(size, data) +} + +func randomImageSize(size int, data []byte) (image.Image, error) { + return wavatars.New(data), nil +} From 47a77348e63edcbf0e2b42bf365bfb01b85952d0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 20 Jul 2022 06:09:32 +0200 Subject: [PATCH 13/13] Revert "add wavatars" This reverts commit 8c6bed3f1956f9e95c8c81fc4223afb6851b23b9. Revert "add monsterid" This reverts commit 309c5e1f6e311c9239497c69bb7a8e4428521a4b. --- go.mod | 2 -- go.sum | 4 ---- modules/avatar/avatar.go | 4 ---- modules/avatar/monsterid/generate.go | 34 ---------------------------- modules/avatar/wavatars/generate.go | 34 ---------------------------- 5 files changed, 78 deletions(-) delete mode 100644 modules/avatar/monsterid/generate.go delete mode 100644 modules/avatar/wavatars/generate.go diff --git a/go.mod b/go.mod index 281e83ea798c8..f3f04ead31686 100644 --- a/go.mod +++ b/go.mod @@ -106,8 +106,6 @@ require ( gopkg.in/ini.v1 v1.66.4 gopkg.in/yaml.v2 v2.4.0 mvdan.cc/xurls/v2 v2.4.0 - src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496 - src.techknowlogick.com/wavatars v0.0.0-20190220172003-d08f03a6b865 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.11 xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f diff --git a/go.sum b/go.sum index 412aa8dbc1f3a..74b39265d8db4 100644 --- a/go.sum +++ b/go.sum @@ -2430,10 +2430,6 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496 h1:c+syTOBF/T6w7vi7KoDpFHQsAxWrZBn2oJfLqylWqgo= -src.techknowlogick.com/monster-id v0.0.0-20210202042845-b14361e87496/go.mod h1:C4IzHjWw8+3/osm2FWUib2Ax2owZ1oKx4rzBZilBS8I= -src.techknowlogick.com/wavatars v0.0.0-20190220172003-d08f03a6b865 h1:X2c3+d3MscxYD2FxBGAYNGmLA0SVtDRWmDFmLADL2Wc= -src.techknowlogick.com/wavatars v0.0.0-20190220172003-d08f03a6b865/go.mod h1:F3KS3mPE6x39Llpd05OjyZnVoQl70UomA7ntxg/jNCQ= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index c48bdefd1b428..650730792497e 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -16,10 +16,8 @@ import ( "code.gitea.io/gitea/modules/avatar/dicebear" "code.gitea.io/gitea/modules/avatar/identicon" - "code.gitea.io/gitea/modules/avatar/monsterid" "code.gitea.io/gitea/modules/avatar/none" "code.gitea.io/gitea/modules/avatar/robot" - "code.gitea.io/gitea/modules/avatar/wavatars" "code.gitea.io/gitea/modules/setting" "github.com/nfnt/resize" @@ -69,8 +67,6 @@ var ( identicon.Identicon{}, none.None{}, robot.Robot{}, - monsterid.Monster{}, - wavatars.Wavatars{}, } ) diff --git a/modules/avatar/monsterid/generate.go b/modules/avatar/monsterid/generate.go deleted file mode 100644 index ec21e0110997f..0000000000000 --- a/modules/avatar/monsterid/generate.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package monsterid - -import ( - "image" - - monster "src.techknowlogick.com/monster-id" -) - -// Monster is used to generate pseudo-random avatars -type Monster struct{} - -func (Monster) Name() string { - return "monsterid" -} - -func (Monster) RandomUserImage(size int, data []byte) (image.Image, error) { - return randomImageSize(size, data) -} - -func (Monster) RandomOrgImage(size int, data []byte) (image.Image, error) { - return randomImageSize(size, data) -} - -func (Monster) RandomRepoImage(size int, data []byte) (image.Image, error) { - return randomImageSize(size, data) -} - -func randomImageSize(size int, data []byte) (image.Image, error) { - return monster.New(data), nil -} diff --git a/modules/avatar/wavatars/generate.go b/modules/avatar/wavatars/generate.go deleted file mode 100644 index ee3015d364eaf..0000000000000 --- a/modules/avatar/wavatars/generate.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package wavatars - -import ( - "image" - - "src.techknowlogick.com/wavatars" -) - -// Wavatars is used to generate pseudo-random avatars -type Wavatars struct{} - -func (Wavatars) Name() string { - return "wavatars" -} - -func (Wavatars) RandomUserImage(size int, data []byte) (image.Image, error) { - return randomImageSize(size, data) -} - -func (Wavatars) RandomOrgImage(size int, data []byte) (image.Image, error) { - return randomImageSize(size, data) -} - -func (Wavatars) RandomRepoImage(size int, data []byte) (image.Image, error) { - return randomImageSize(size, data) -} - -func randomImageSize(size int, data []byte) (image.Image, error) { - return wavatars.New(data), nil -}