From 692f5a85afe691c83ca359f2818e1bccbf43767a Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Wed, 28 Aug 2019 09:20:53 -0400 Subject: Clean up --- README.md | 32 +++++++++++++++++++++++++ main.go | 80 +++++++++++++++++++++++++++++++++------------------------------ 2 files changed, 74 insertions(+), 38 deletions(-) create mode 100644 README.md 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)) } } } -- cgit v1.2.3