identicond/vendor/github.com/jakobvarmose/go-qidenticon/qidenticon.go

142 lines
4.7 KiB
Go

package qidenticon
import (
"crypto/sha512"
"encoding/binary"
"image"
"image/color"
"math"
"github.com/fogleman/gg"
)
// Code derives a code for use with Render.
func Code(str string) uint64 {
buf := sha512.Sum512([]byte(str))
return binary.BigEndian.Uint64(buf[56:])
}
type Settings struct {
// TwoColor specifies if the identicon should be
// generated using one or two colors.
TwoColor bool
// Alpha specifies the transparency of the generated identicon.
Alpha uint8
}
// DefaultSettings returns a Settings object with the recommended settings.
func DefaultSettings() *Settings {
return &Settings{
TwoColor: true,
Alpha: 255,
}
}
// Render generates an identicon.
// code is a code derived by the Code function.
// totalSize specifies the total size in pixels. It is recommended that
// this is divisible by 3.
func Render(code uint64, totalSize int, settings *Settings) image.Image {
penWidth := 0
middleType := int(code & 0x03)
middleInvert := code>>2&0x01 == 1
cornerType := int(code >> 3 & 0x0f)
cornerInvert := code>>7&0x01 == 1
cornerTurn := int(code >> 8 & 0x03)
sideType := int(code >> 10 & 0x0f)
sideInvert := code>>14&0x01 == 1
sideTurn := int(code >> 15 & 0x03)
blue := code >> 17 & 0x1f
green := code >> 22 & 0x1f
red := code >> 27 & 0x1f
secondRed := code >> 32 & 0x1f
secondGreen := code >> 37 & 0x1f
secondBlue := code >> 42 & 0x1f
swapCross := code>>47&0x01 == 1
middleType = middlePatchSet[middleType]
foreColor := color.RGBA{R: uint8(red) << 3, G: uint8(green) << 3, B: uint8(blue) << 3, A: settings.Alpha}
var secondColor color.RGBA
if settings.TwoColor {
secondColor = color.RGBA{R: uint8(secondRed) << 3, G: uint8(secondGreen) << 3, B: uint8(secondBlue) << 3, A: settings.Alpha}
} else {
secondColor = foreColor
}
var middleColor color.Color
if swapCross {
middleColor = foreColor
} else {
middleColor = secondColor
}
image := gg.NewContext(totalSize, totalSize)
patchSize := float64(totalSize) / 3
drawPatch(gg.Point{X: 1, Y: 1}, 0, middleInvert, middleType, image, patchSize, middleColor, penWidth)
for i, p := range []gg.Point{{X: 1, Y: 0}, {X: 2, Y: 1}, {X: 1, Y: 2}, {X: 0, Y: 1}} {
drawPatch(p, sideTurn+1+i, sideInvert, sideType, image, patchSize, foreColor, penWidth)
}
for i, p := range []gg.Point{{X: 0, Y: 0}, {X: 2, Y: 0}, {X: 2, Y: 2}, {X: 0, Y: 2}} {
drawPatch(p, cornerTurn+1+i, cornerInvert, cornerType, image, patchSize, secondColor, penWidth)
}
return image.Image()
}
func drawPatch(pos gg.Point, turn int, invert bool, type_ int, image *gg.Context, patchSize float64, foreColor color.Color, penWidth int) {
path := pathSet[type_]
turn %= 4
image.Push()
image.Translate(pos.X*patchSize+float64(penWidth)/2, pos.Y*patchSize+float64(penWidth)/2)
image.RotateAbout(float64(turn)*math.Pi/2, patchSize/2, patchSize/2)
for _, p := range path {
image.LineTo(p.X/4*patchSize, p.Y/4*patchSize)
}
image.ClosePath()
if invert {
image.MoveTo(0, 0)
image.LineTo(0, patchSize)
image.LineTo(patchSize, patchSize)
image.LineTo(patchSize, 0)
image.ClosePath()
}
image.SetColor(foreColor)
image.Fill()
image.Pop()
}
var pathSet = [][]gg.Point{
// [0] full square:
{{X: 0, Y: 0}, {X: 4, Y: 0}, {X: 4, Y: 4}, {X: 0, Y: 4}},
// [1] right-angled triangle pointing top-left:
{{X: 0, Y: 0}, {X: 4, Y: 0}, {X: 0, Y: 4}},
// [2] upwardy triangle:
{{X: 2, Y: 0}, {X: 4, Y: 4}, {X: 0, Y: 4}},
// [3] left half of square, standing rectangle:
{{X: 0, Y: 0}, {X: 2, Y: 0}, {X: 2, Y: 4}, {X: 0, Y: 4}},
// [4] square standing on diagonale:
{{X: 2, Y: 0}, {X: 4, Y: 2}, {X: 2, Y: 4}, {X: 0, Y: 2}},
// [5] kite pointing topleft:
{{X: 0, Y: 0}, {X: 4, Y: 2}, {X: 4, Y: 4}, {X: 2, Y: 4}},
// [6] Sierpinski triangle, fractal triangles:
{{X: 2, Y: 0}, {X: 4, Y: 4}, {X: 2, Y: 4}, {X: 3, Y: 2}, {X: 1, Y: 2}, {X: 2, Y: 4}, {X: 0, Y: 4}},
// [7] sharp angled lefttop pointing triangle:
{{X: 0, Y: 0}, {X: 4, Y: 2}, {X: 2, Y: 4}},
// [8] small centered square:
{{X: 1, Y: 1}, {X: 3, Y: 1}, {X: 3, Y: 3}, {X: 1, Y: 3}},
// [9] two small triangles:
{{X: 2, Y: 0}, {X: 4, Y: 0}, {X: 0, Y: 4}, {X: 0, Y: 2}, {X: 2, Y: 2}},
// [10] small topleft square:
{{X: 0, Y: 0}, {X: 2, Y: 0}, {X: 2, Y: 2}, {X: 0, Y: 2}},
// [11] downpointing right-angled triangle on bottom:
{{X: 0, Y: 2}, {X: 4, Y: 2}, {X: 2, Y: 4}},
// [12] uppointing right-angled triangle on bottom:
{{X: 2, Y: 2}, {X: 4, Y: 4}, {X: 0, Y: 4}},
// [13] small rightbottom pointing right-angled triangle on topleft:
{{X: 2, Y: 0}, {X: 2, Y: 2}, {X: 0, Y: 2}},
// [14] small lefttop pointing right-angled triangle on topleft:
{{X: 0, Y: 0}, {X: 2, Y: 0}, {X: 0, Y: 2}},
// [15] empty:
{},
}
// get the [0] full square, [4] square standing on diagonale, [8] small centered square, or [15] empty tile:
var middlePatchSet = []int{0, 4, 8, 15}