diff --git a/README.adoc b/README.adoc
index 0967ea3..d33fa33 100644
--- a/README.adoc
+++ b/README.adoc
@@ -34,6 +34,8 @@ The latter resembles the color scheme used by GitHub.
The request query parameter `d` can be used to request GitHub like patterns by setting the value to `github`.
+Using query param `ct` with value `svg` or using request header `Accept: image/svg+xml` will generate SVG identicon.
+
==== Examples
Some examples for `/avatar/example` and different params.
diff --git a/icons/ghicons.go b/icons/ghicons.go
index acadcc3..1b7d203 100644
--- a/icons/ghicons.go
+++ b/icons/ghicons.go
@@ -42,6 +42,29 @@ func (generator *GhIconGenerator) GenIcon(id string, size int) *image.NRGBA {
return drawImage(mirrorData(data, blocks), blocks, size, generator.colorGenerator(hash))
}
+func (generator *GhIconGenerator) GenSvg(id string, size int) string {
+ if size > 512 {
+ size = 512
+ }
+ blocks := 5
+
+ hash := HashBytes(id)
+ nibbles := nibbles(hash)
+ data := make([]bool, blocks*blocks)
+
+ for x := 0; x < blocks; x++ {
+ for y := 0; y < blocks; y++ {
+ ni := x + blocks*(blocks-y-1)
+ if x+blocks*y >= 2*blocks {
+ di := (x + blocks*y) - 2*blocks
+ data[di] = nibbles[ni%32]%2 == 0
+ }
+ }
+ }
+
+ return drawSvg(mirrorData(data, blocks), blocks, size, generator.colorGenerator(hash))
+}
+
// https://processing.org/reference/map_.html
func remap(value uint32, vmin uint32, vmax uint32, dmin uint32, dmax uint32) float32 {
return float32((value-vmin)*(dmax-dmin)) / float32((vmax-vmin)+dmin)
diff --git a/icons/icons.go b/icons/icons.go
index 4b857fc..04f68ec 100644
--- a/icons/icons.go
+++ b/icons/icons.go
@@ -3,6 +3,7 @@ package icons
import (
"crypto/md5"
"encoding/hex"
+ "fmt"
"image"
"image/color"
"image/draw"
@@ -12,6 +13,7 @@ import (
type IconGenerator interface {
GenIcon(id string, size int) *image.NRGBA
+ GenSvg(id string, size int) string
}
func HashBytes(id string) [16]byte {
@@ -62,5 +64,37 @@ func drawImage(data []bool, blocks int, size int, c color.Color) *image.NRGBA {
}
}
+ drawSvg(data, blocks, size, c)
+
return img
}
+
+func drawSvg(data []bool, blocks int, size int, c color.Color) string {
+
+ blockSize := size / (blocks + 1)
+ border := (size - (blocks * blockSize)) / 2
+ r, g, b, _ := c.RGBA()
+ colorHtml := fmt.Sprintf("#%x%x%x", r>>8, g>>8, b>>8)
+
+ blockElems := fmt.Sprintf("", size, size)
+
+ for x := 0; x < blocks; x++ {
+ for y := 0; y < blocks; y++ {
+ idx := x*blocks + y
+ if data[idx] {
+ blockElems += fmt.Sprintf(
+ ``,
+ colorHtml,
+ blockSize,
+ blockSize,
+ border+(x*blockSize),
+ border+(y*blockSize))
+ }
+ }
+ }
+
+ return fmt.Sprintf(`
+`,
+ size, size,
+ blockElems)
+}
diff --git a/icons/idicons.go b/icons/idicons.go
index c3f9bc8..7fa7dad 100644
--- a/icons/idicons.go
+++ b/icons/idicons.go
@@ -34,6 +34,21 @@ func (generator *IdIconGenerator) GenIcon(id string, size int) *image.NRGBA {
return drawImage(mirrorData(data, blocks), blocks, size, generator.colorGenerator(hash))
}
+func (generator *IdIconGenerator) GenSvg(id string, size int) string {
+ id = strings.ToLower(id)
+ blocks := 5
+ if size > 512 {
+ size = 512
+ }
+
+ hash := HashBytes(id)
+ data := make([]bool, blocks*blocks)
+ for i := 0; i < len(hash)-1; i++ {
+ data[i] = hash[i]%2 != hash[i+1]%2
+ }
+ return drawSvg(mirrorData(data, blocks), blocks, size, generator.colorGenerator(hash))
+}
+
func ColorV1(hash [16]byte) color.RGBA {
r := 32 + (hash[0]%16)/2<<4
g := 32 + (hash[2]%16)/2<<4
diff --git a/idicon.go b/idicon.go
index b6d60ed..568d3a6 100644
--- a/idicon.go
+++ b/idicon.go
@@ -62,7 +62,6 @@ func requestHandler(w http.ResponseWriter, r *http.Request) {
}
}
- w.Header().Add("Content-Type", "image/png")
cFunc := icons.ColorV2
if colorScheme == "v1" {
cFunc = icons.ColorV1
@@ -77,7 +76,15 @@ func requestHandler(w http.ResponseWriter, r *http.Request) {
iconGenerator = icons.NewIdIconGenerator().WithColorGenerator(cFunc)
}
- err = png.Encode(w, iconGenerator.GenIcon(id, size))
+ ct := r.URL.Query().Get("ct")
+ cth := r.Header.Get("Accept")
+ if ct == "svg" || cth == "image/svg+xml" {
+ w.Header().Add("Content-Type", "image/svg+xml")
+ _, err = w.Write([]byte(iconGenerator.GenSvg(id, size)))
+ } else {
+ w.Header().Add("Content-Type", "image/png")
+ err = png.Encode(w, iconGenerator.GenIcon(id, size))
+ }
}
var (
diff --git a/static/index.html b/static/index.html
index 0a7eb3e..7e18aa8 100644
--- a/static/index.html
+++ b/static/index.html
@@ -108,18 +108,18 @@
width: 90%;
}
- #size-input, #color-input, #type-input {
+ #size-input, #color-input, #type-input, #contenttype-input {
display: flex;
flex-direction: column;
margin: 0 auto;
}
- #size-input > input, #color-input > select, #type-input > select {
+ #size-input > input, #color-input > select, #type-input > select, #contenttype-input > select {
width: 8em;
}
#settings {
- grid-template-columns: repeat(3, 1fr);
+ grid-template-columns: repeat(4, 1fr);
display: grid;
}
@@ -185,6 +185,13 @@
+
+
+
+