Skip to content

Commit 068cd69

Browse files
committed
Refactor public API
1 parent b41e73c commit 068cd69

File tree

5 files changed

+84
-41
lines changed

5 files changed

+84
-41
lines changed

avatars.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,30 @@ package avatars
33
import (
44
"bytes"
55
"html"
6+
"image"
67
"image/png"
78

89
"github.com/lafriks/go-avatars/bottts"
10+
"github.com/lafriks/go-avatars/rnd"
911

1012
"github.com/fogleman/gg"
1113
"github.com/lafriks/go-svg"
1214
"github.com/lafriks/go-svg/renderer"
1315
rendr_gg "github.com/lafriks/go-svg/renderer/gg"
1416
)
1517

16-
// SVG generates an SVG avatar using the given name as seed.
17-
func SVG(name string, opts ...Option) ([]byte, error) {
18-
attrs, body := bottts.Generate(name)
18+
// Avatar that was generated using specified seed.
19+
type Avatar struct {
20+
r *rnd.Random
21+
name string
22+
svg []byte
23+
}
24+
25+
// Generate an avatar using the given name as seed.
26+
func Generate(name string, opts ...Option) (*Avatar, error) {
27+
r := rnd.NewRandom(name)
28+
29+
attrs, body := bottts.Generate(r)
1930

2031
res := bytes.Buffer{}
2132
res.WriteString("<svg")
@@ -29,23 +40,32 @@ func SVG(name string, opts ...Option) ([]byte, error) {
2940
res.WriteString(">\n")
3041
res.WriteString(body)
3142
res.WriteString("\n</svg>")
32-
return res.Bytes(), nil
43+
return &Avatar{
44+
name: name,
45+
r: r,
46+
svg: res.Bytes(),
47+
}, nil
3348
}
3449

35-
// PNG generates an PNG avatar using the given name as seed.
36-
func PNG(name string, opts ...Option) ([]byte, error) {
37-
avatar, err := SVG(name, opts...)
38-
if err != nil {
39-
return nil, err
40-
}
50+
// Name of the avatar.
51+
func (a *Avatar) Name() string {
52+
return a.name
53+
}
4154

42-
opt := options(opts...)
55+
// SVG content of the avatar.
56+
func (a *Avatar) SVG() []byte {
57+
return a.svg
58+
}
4359

44-
s, err := svg.Parse(bytes.NewReader(avatar), svg.IgnoreErrorMode)
60+
// Image rendered from the SVG.
61+
func (a *Avatar) Image(opts ...RenderOption) (image.Image, error) {
62+
s, err := svg.Parse(bytes.NewReader(a.svg), svg.IgnoreErrorMode)
4563
if err != nil {
4664
return nil, err
4765
}
4866

67+
opt := renderOptions(opts...)
68+
4969
gc := gg.NewContext(opt.Size, opt.Size)
5070
rendr_gg.Draw(gc, s, renderer.Target(
5171
float64(opt.Padding),
@@ -54,8 +74,18 @@ func PNG(name string, opts ...Option) ([]byte, error) {
5474
float64(opt.Size-opt.Padding*2),
5575
))
5676

77+
return gc.Image(), nil
78+
}
79+
80+
// PNG encoded image.
81+
func (a *Avatar) PNG(opts ...RenderOption) ([]byte, error) {
82+
img, err := a.Image(opts...)
83+
if err != nil {
84+
return nil, err
85+
}
86+
5787
buf := bytes.NewBuffer(nil)
58-
if err := png.Encode(buf, gc.Image()); err != nil {
88+
if err := png.Encode(buf, img); err != nil {
5989
return nil, err
6090
}
6191
return buf.Bytes(), nil

bottts/avatar.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,22 @@ func group(r *rnd.Random, content string, chance, x, y int) string {
2121
}
2222

2323
// Generate generates a bottts avatar.
24-
func Generate(seed string) (map[string]string, string) {
25-
r := rnd.NewRandom(seed)
26-
24+
func Generate(r *rnd.Random) (map[string]string, string) {
2725
var texture string
2826
if r.Chance(textureChance) {
2927
texture = textures[r.Pick(textureNames)]
3028
}
3129

3230
primaryColor, secondaryColor := r.PickColors(600, 400)
3331

34-
body := []string{
35-
group(r, sides[r.Pick(sideNames)](secondaryColor), sidesChance, 0, 66),
36-
group(r, tops[r.Pick(topNames)](secondaryColor), topChance, 41, 0),
37-
group(r, face[r.Pick(faceNames)](primaryColor, texture), 100, 25, 44),
38-
group(r, mouth[r.Pick(mouthNames)], 100, 52, 124),
39-
group(r, eyes[r.Pick(eyeNames)], 100, 38, 76),
40-
}
32+
var buf strings.Builder
33+
buf.WriteString(group(r, sides[r.Pick(sideNames)](secondaryColor), sidesChance, 0, 66))
34+
buf.WriteString(group(r, tops[r.Pick(topNames)](secondaryColor), topChance, 41, 0))
35+
buf.WriteString(group(r, face[r.Pick(faceNames)](primaryColor, texture), 100, 25, 44))
36+
buf.WriteString(group(r, mouth[r.Pick(mouthNames)], 100, 52, 124))
37+
buf.WriteString(group(r, eyes[r.Pick(eyeNames)], 100, 38, 76))
38+
4139
return map[string]string{
4240
"viewBox": "0 0 180 180",
43-
}, strings.Join(body, "")
41+
}, buf.String()
4442
}

example/sample/main.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"bytes"
54
"image"
65
"image/draw"
76
"image/png"
@@ -16,12 +15,11 @@ func main() {
1615
img := image.NewRGBA(image.Rect(0, 0, size*10, size*4))
1716

1817
for i := 0; i < 40; i++ {
19-
seed := time.Now().Format(time.RFC3339Nano)
20-
buf, err := avatars.PNG(seed, avatars.RenderSize(size))
18+
a, err := avatars.Generate(time.Now().Format(time.RFC3339Nano))
2119
if err != nil {
2220
panic(err)
2321
}
24-
av, err := png.Decode(bytes.NewReader(buf))
22+
av, err := a.Image(avatars.RenderSize(size))
2523
if err != nil {
2624
panic(err)
2725
}

example/test/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ func main() {
2121
}
2222
seed := time.Now().Format(time.RFC3339Nano)
2323

24-
svg, err := avatars.SVG(seed)
24+
a, err := avatars.Generate(seed)
2525
if err != nil {
2626
panic(err)
2727
}
28-
if err := ioutil.WriteFile("test.svg", svg, fs.FileMode(0644)); err != nil {
28+
if err := ioutil.WriteFile("test.svg", a.SVG(), fs.FileMode(0644)); err != nil {
2929
panic(err)
3030
}
31-
png, err := avatars.PNG(seed, avatars.RenderSize(size))
31+
img, err := a.PNG(avatars.RenderSize(size))
3232
if err != nil {
3333
panic(err)
3434
}
35-
if err := ioutil.WriteFile("test.png", png, fs.FileMode(0644)); err != nil {
35+
if err := ioutil.WriteFile("test.png", img, fs.FileMode(0644)); err != nil {
3636
panic(err)
3737
}
3838
}

options.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,50 @@ package avatars
22

33
// Options for the avatar service.
44
type Options struct {
5+
}
6+
7+
// Option is a function that applies an option to the avatar service.
8+
type Option interface {
9+
apply(a *Options)
10+
}
11+
12+
func options(opts ...Option) *Options {
13+
opt := &Options{}
14+
for _, o := range opts {
15+
o.apply(opt)
16+
}
17+
return opt
18+
}
19+
20+
// RenderOptions is a set of options for rendering an avatar.
21+
type RenderOptions struct {
522
// Size of the rendered avatar.
623
Size int
724
// Padding between the rendered avatar and the border.
825
Padding int
926
}
1027

11-
// Option is a function that applies an option to the avatar service.
12-
type Option interface {
13-
apply(a *Options)
28+
// Render is a function that applies a render option to the avatar service.
29+
type RenderOption interface {
30+
apply(a *RenderOptions)
1431
}
1532

1633
// RenderSize sets the size of the rendered avatar in pixels.
1734
type RenderSize int
1835

19-
func (s RenderSize) apply(a *Options) {
36+
func (s RenderSize) apply(a *RenderOptions) {
2037
a.Size = int(s)
2138
}
2239

23-
// Padding sets the padding between the rendered avatar and the border.
24-
type Padding int
40+
// RenderPadding sets the padding between the rendered avatar and the border.
41+
type RenderPadding int
2542

26-
func (s Padding) apply(a *Options) {
43+
func (s RenderPadding) apply(a *RenderOptions) {
2744
a.Padding = int(s)
2845
}
2946

30-
func options(opts ...Option) *Options {
31-
opt := &Options{
47+
func renderOptions(opts ...RenderOption) *RenderOptions {
48+
opt := &RenderOptions{
3249
Size: 256,
3350
}
3451
for _, o := range opts {

0 commit comments

Comments
 (0)