package main import ( "crypto/md5" "crypto/sha1" "fmt" "math" "slices" "strconv" "github.com/fogleman/gg" ) func main() { dc := gg.NewContext(800, 800) GitHub(dc, "64578396") // XBitPolygons(dc, "BigBor14", 6, 6) // FourBitSquares(dc, "Jaybee18") dc.SavePNG("out.png") } // Bit font character overlay mit xor func hue_to_rgb(a float32, b float32, hue float32) float32 { var h float32 if hue < 0.0 { h = hue + 1.0 } else if hue > 1.0 { h = hue - 1.0 } else { h = hue } if h < 1.0 / 6.0 { return a + (b - a) * 6.0 * h } if h < 1.0 / 2.0 { return b } if h < 2.0 / 3.0 { return a + (b - a) * (2.0 / 3.0 - h) * 6.0 } return a } func foreground(source []uint8) (int, int, int) { h1 := (uint16(source[12]) & 0x0f) << 8; h2 := uint16(source[13]) h := uint32(h1 | h2) s := uint32(source[14]) l := uint32(source[15]) hue := float32(h * 360) / float32(4095) sat := float32(s * 20) / float32(255) lum := float32(l * 20) / float32(255) sat = 65.0 - sat lum = 75.0 - lum hue = hue / 360.0 sat = sat / 100.0 lum = lum / 100.0 var b float32 if lum <= 0.5 { b = lum * (sat + 1.0) } else { b = lum + sat - lum * sat } a := lum * 2.0 - b r := hue_to_rgb(a, b, hue + 1.0 / 3.0) g := hue_to_rgb(a, b, hue) b = hue_to_rgb(a, b, hue - 1.0 / 3.0) return int(math.Round(float64(r * 255))), int(math.Round(float64(g * 255))), int(math.Round(float64(b * 255))) } // Translated to Go from: https://github.com/dgraham/identicon func GitHub(dc *gg.Context, userId string) { fmt.Printf("md5.Sum([]byte(userId)): %v\n", md5.Sum([]byte(userId))) hash := md5.Sum([]byte(userId)) // userIdHash := "da02efbbbe97337447671769e0f50c4a" // ohne newline (richtiges symbol) w := 800 h := 800 cols := 5 rows := 5 pw := w / cols ph := h / rows // Background dc.SetRGB255(240, 240, 240) dc.DrawRectangle(0, 0, float64(w), float64(h)) dc.Fill() // Convert to bytes // bytes := []uint8{} // for i := 0; i < 32; i+=2 { // integer, _ := strconv.ParseUint(string(userIdHash[i]) + string(userIdHash[i+1]), 16, 8) // bytes = append(bytes, uint8(integer)) // } // fmt.Println(bytes) dc.SetRGB255(foreground(hash[:])) // Foreground enabledVertices := make([]bool, 25) // for i, character := range userIdHash[:15] { // integer, _ := strconv.ParseUint(string(character), 16, 8) // fmt.Println(integer) // painted := integer % 2 == 0 // rowIdx := i % 5 // colIdx := 2 - i / 5 // mirrColIdx := 2 + i / 5 // enabledVertices[rowIdx * 5 + colIdx] = painted // enabledVertices[rowIdx * 5 + mirrColIdx] = painted // } for i := range 15 { // integer, _ := strconv.ParseUint(string(character), 16, 8) byte := hash[i/2] var nibble uint8 // 4 bits are called a nibble if i % 2 == 0 { nibble = (byte & 0xf0) >> 4 // left side } else { nibble = byte & 0x0f // right side } painted := nibble % 2 == 0 rowIdx := i % 5 colIdx := 2 - i / 5 mirrColIdx := 2 + i / 5 enabledVertices[rowIdx * 5 + colIdx] = painted enabledVertices[rowIdx * 5 + mirrColIdx] = painted } // 216 166 130 // Foreground color // da02efbbbe97337447671769e0f50c4a // h1 = 3 -> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 -> 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 // h2 = 3 -> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 // h = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 = 771 // s = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 = 7 // l = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 = 4 // hue = 277560 / 4095 = 67,78021978 (73710) // sat = 140 / 255 = 0,54901961 // lum = 80 / 255 = 0,31372549 // // 67.78021978, 64.45098039, 74.68627451 // --- // // l = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 1 1 1 0 0 0 1 = 23409 // hue = 73710 / 4095 = 21.538462 // sat = 3213 / 255 = 12.6 // lum = 1836 / 255 = 7.2 // Solution: 18, 64.3, 69.2 for row, pix := range slices.Collect(slices.Chunk(enabledVertices, 5)) { for col, painted := range pix { if painted { x := col * pw y := row * ph dc.DrawRectangle(float64(x), float64(y), float64(pw), float64(ph)) dc.Fill() } } } } func XBitPolygons(dc *gg.Context, username string, columns int, rows int) { hash := hash(username) dc.SetRGB255(255, 255, 255) dc.DrawRectangle(0, 0, 800, 800) dc.Fill() colorString := hash[0:6] r, g, b := rgbFromHex(colorString) dc.SetRGB255(r, g, b) enabledVertices := [][]bool{} for _, character := range hash[6:6+rows+1] { integer, _ := strconv.ParseUint(string(character), 16, columns/2+1) row := []bool{} for j := range columns/2+1 { on := hasBit(integer, j) row = append(row, on) } enabledVertices = append(enabledVertices, row) } w := 800 / columns h := 800 / rows for i := range rows { for j := range columns/2 { topLeft, tlx, tly := enabledVertices[i][j], float64(j*w), float64(i*h) topRight, trx, try := enabledVertices[i][j+1], float64(j*w+w), float64(i*h) bottomRight, brx, bry := enabledVertices[i+1][j+1], float64(j*w+w), float64(i*h+h) bottomLeft, blx, bly := enabledVertices[i+1][j], float64(j*w), float64(i*h+h) mx, my := tlx + float64(w/2), tly + float64(h/2) count := 0 if topLeft {count++} if topRight {count++} if bottomRight {count++} if bottomLeft {count++} if topLeft && topRight { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(tlx, tly) dc.LineTo(trx, try) dc.ClosePath() } if topRight && bottomRight { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(trx, try) dc.LineTo(brx, bry) dc.ClosePath() } if bottomRight && bottomLeft { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(brx, bry) dc.LineTo(blx, bly) dc.ClosePath() } if bottomLeft && topLeft { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(blx, bly) dc.LineTo(tlx, tly) dc.ClosePath() } dc.Fill() } } // Reverse for i := range len(enabledVertices) { slices.Reverse(enabledVertices[i]) } // Mirror left side for i := range rows { for j := range columns/2 { topLeft, tlx, tly := enabledVertices[i][j], float64(w*(columns/2)+j*w), float64(i*h) topRight, trx, try := enabledVertices[i][j+1], float64(w*(columns/2)+j*w+w), float64(i*h) bottomRight, brx, bry := enabledVertices[i+1][j+1], float64(w*(columns/2)+j*w+w), float64(i*h+h) bottomLeft, blx, bly := enabledVertices[i+1][j], float64(w*(columns/2)+j*w), float64(i*h+h) mx, my := tlx+float64(w/2), tly+float64(h/2) count := 0 if topLeft {count++} if topRight {count++} if bottomRight {count++} if bottomLeft {count++} if topLeft && topRight { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(tlx, tly) dc.LineTo(trx, try) dc.ClosePath() } if topRight && bottomRight { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(trx, try) dc.LineTo(brx, bry) dc.ClosePath() } if bottomRight && bottomLeft { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(brx, bry) dc.LineTo(blx, bly) dc.ClosePath() } if bottomLeft && topLeft { dc.NewSubPath() dc.MoveTo(mx, my) dc.LineTo(blx, bly) dc.LineTo(tlx, tly) dc.ClosePath() } dc.Fill() } } } /* 8x8 square of 100x100 px squares First 6 characters of the hash are the color For the next 8 characters each character represents 4 bits of a row being on or off. The image is then mirrored to the right side. 9d26bf3cf1fb2b2f02d4a2013333f4fcd551cb2e 9d26bf -> color 3 -> row1 c -> row2 ... b -> row8 3 = 0 0 1 1 c = 1 1 0 0 ... b = 1 0 1 1 */ func FourBitSquares(dc *gg.Context, username string) { hash := hash(username) colorString := hash[0:6] r, g, b := rgbFromHex(colorString) dc.SetRGB255(r, g, b) for i, character := range hash[6:14] { integer, _ := strconv.ParseUint(string(character), 16, 4) for j := range 4 { on := hasBit(integer, j) dc.DrawRectangle(float64(j * 100), float64(i * 100), 100, 100) dc.DrawRectangle(700 - float64(j * 100), float64(i * 100), 100, 100) if !on { dc.SetRGB255(r, g, b) } else { dc.SetRGB255(255, 255, 255) } dc.Fill() } } } func rgbFromHex(hex string) (int, int, int) { r, _ := strconv.ParseUint(hex[0:2], 16, 8) g, _ := strconv.ParseUint(hex[2:4], 16, 8) b, _ := strconv.ParseUint(hex[4:6], 16, 8) return int(r), int(g), int(b) } func hash(str string) string { h := sha1.New() h.Write([]byte(str)) res := h.Sum(nil) return fmt.Sprintf("%x", res) } func hasBit(n uint64, pos int) bool { val := n & (1 << pos) return (val > 0) } func Map[T any, M any](a []T, f func(T) M) []M { n := make([]M, len(a)) for i, e := range a { n[i] = f(e) } return n }