aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2019-08-28 09:20:53 -0400
committerBen Burwell <ben@benburwell.com>2019-08-28 09:20:53 -0400
commit692f5a85afe691c83ca359f2818e1bccbf43767a (patch)
tree6acaf74d8cd49072d76b733dbfb4dcd950660b99
parentd5f8481cfa2c22a96f9ae3035d4107d3b19ca61f (diff)
Clean up
-rw-r--r--README.md32
-rw-r--r--main.go80
2 files changed, 74 insertions, 38 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8e6bab1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# hashimg
+
+Written for Boston Golang "Chopped" meetup. Given the following packages (and
+any of their subpackages), make anything:
+
+* bytes
+* crypto
+* encoding
+* image
+* regexp
+
+Additionally, the following packages from the standard library were available:
+
+* flag
+* fmt
+
+I ended up using all of the "basket" packages, even where not strictly
+necessary; e.g. the only real reason to base64 encode the resulting image is
+just so that we can do something with the encoding package. Likewise, we don't
+_really_ need a regular expression to validate the input, but why not throw it
+in?
+
+Given a "username", this program finds the SHA 256 checksum of the username and
+uses it to generate a GitHub-style default avatar. The first three bytes are
+used to create color A, the next three bytes are used to create color B, and the
+next 25 bytes are used to form a 5x5 pixel grid, with the value being used to
+determine whether the pixel should be colored with A or B.
+
+Next, the 5x5 pixel image is tesselated into a 10x10 pixel image for some neat
+mirroring, and then it is scaled up by a user-provided multiplier.
+
+Finally, the image is base64 encoded and printed to standard output.
diff --git a/main.go b/main.go
index 148ce16..7e59b1b 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
package main
import (
+ // Bag 4: bytes, crypto, encoding, image, regexp
"bytes"
"crypto/sha256"
"encoding/base64"
@@ -9,54 +10,55 @@ import (
"image/png"
"regexp"
+ // Pantry: flag
"flag"
"fmt"
- "strings"
)
-var usernamePattern = regexp.MustCompile("[a-zA-Z][a-zA-Z0-9]+")
+var usernamePattern = regexp.MustCompile("[a-zA-Z][a-zA-Z0-9]*")
func main() {
username := flag.String("username", "", "username to encode as image (must contain only letters a-zA-Z0-9 and start with a letter)")
+ mult := flag.Int("mult", 20, "the multiplier to apply to the 10x10 generated image (must be >= 1)")
flag.Parse()
- if strings.TrimSpace(*username) == "" {
+
+ if !usernamePattern.MatchString(*username) {
flag.Usage()
return
}
- if !usernamePattern.MatchString(*username) {
+ if *mult < 1 {
flag.Usage()
return
}
- seed := hash(*username)
- img := makeBigger(repeat(generateImage(seed)))
- str, err := encodeImage(img)
+ hash := sha256.Sum256([]byte(*username))
+ img := makeBigger(tesselate(generateImage(hash)), *mult)
+ str, err := encodePNG(img)
if err != nil {
- fmt.Printf("%v\n", err)
+ fmt.Println(err)
return
}
- fmt.Println(str)
-}
-func hash(username string) [32]byte {
- return sha256.Sum256([]byte(username))
+ fmt.Println(str)
}
-func generateImage(hash [32]byte) image.Image {
- img := image.NewNRGBA(image.Rect(0, 0, 5, 5))
- colorA := color.NRGBA{
- R: hash[0],
- G: hash[1],
- B: hash[2],
- A: 255,
+func newColor(b []byte) color.NRGBA {
+ if len(b) != 3 {
+ panic("cannot make color without exactly 3 bytes")
}
- colorB := color.NRGBA{
- R: hash[3],
- G: hash[4],
- B: hash[5],
+ return color.NRGBA{
+ R: b[0],
+ G: b[1],
+ B: b[2],
A: 255,
}
+}
+
+func generateImage(hash [32]byte) *image.NRGBA {
+ img := image.NewNRGBA(image.Rect(0, 0, 5, 5))
+ colorA := newColor(hash[0:3])
+ colorB := newColor(hash[3:6])
offset := 6
for x := 0; x < 5; x++ {
for y := 0; y < 5; y++ {
@@ -72,7 +74,7 @@ func generateImage(hash [32]byte) image.Image {
return img
}
-func encodeImage(img image.Image) (string, error) {
+func encodePNG(img *image.NRGBA) (string, error) {
var b bytes.Buffer
if err := png.Encode(&b, img); err != nil {
return "", err
@@ -80,26 +82,28 @@ func encodeImage(img image.Image) (string, error) {
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
}
-func repeat(img image.Image) image.Image {
- newImg := image.NewNRGBA(image.Rect(0, 0, 10, 10))
- for x := 0; x < 5; x++ {
- for y := 0; y < 5; y++ {
+func tesselate(img *image.NRGBA) *image.NRGBA {
+ newImg := image.NewNRGBA(image.Rect(0, 0, img.Bounds().Max.X*2, img.Bounds().Max.Y*2))
+ for x := 0; x < img.Bounds().Max.X; x++ {
+ for y := 0; y < img.Bounds().Max.Y; y++ {
+ x1 := img.Bounds().Max.X*2 - 1
+ y1 := img.Bounds().Max.Y*2 - 1
newImg.Set(x, y, img.At(x, y))
- newImg.Set(9-x, y, img.At(x, y))
- newImg.Set(x, 9-y, img.At(x, y))
- newImg.Set(9-x, 9-y, img.At(x, y))
+ newImg.Set(x1-x, y, img.At(x, y))
+ newImg.Set(x, y1-y, img.At(x, y))
+ newImg.Set(x1-x, y1-y, img.At(x, y))
}
}
return newImg
}
-func makeBigger(img image.Image) image.Image {
- newImg := image.NewNRGBA(image.Rect(0, 0, 200, 200))
- for x := 0; x < 10; x++ {
- for y := 0; y < 10; y++ {
- for i := 0; i < 20; i++ {
- for j := 0; j < 20; j++ {
- newImg.Set(x*20+i, y*20+j, img.At(x, y))
+func makeBigger(img *image.NRGBA, mult int) *image.NRGBA {
+ newImg := image.NewNRGBA(image.Rect(0, 0, img.Bounds().Max.X*mult, img.Bounds().Max.Y*mult))
+ for x := 0; x < img.Bounds().Max.X; x++ {
+ for y := 0; y < img.Bounds().Max.Y; y++ {
+ for i := 0; i < mult; i++ {
+ for j := 0; j < mult; j++ {
+ newImg.Set(x*mult+i, y*mult+j, img.At(x, y))
}
}
}