Skip to content

Instantly share code, notes, and snippets.

@lunaisnotaboy
Created July 20, 2022 23:36
Show Gist options
  • Save lunaisnotaboy/3c84e50cc9c8c7ab619da9b09220f5bd to your computer and use it in GitHub Desktop.
Save lunaisnotaboy/3c84e50cc9c8c7ab619da9b09220f5bd to your computer and use it in GitHub Desktop.
A small Ruby class for generating the identicons that GitHub uses. Requires the `color`, `chunky_png`, and `oily_png` gems.
# frozen_string_literal: true
require 'color'
require 'oily_png'
class Identicon
# How big each "pixel" is
PIXEL_SIZE = 70
# How many rows and columns
SPRITE_SIZE = 5
def initialize(gravatar_id)
@hash = gravatar_id
@sprite_color = nil
generate_color
end
def generate_color
# Use values at the end of the hash to determine HSL values
hue = map(@hash[25, 3].to_i(16), 0, 4095, 0, 360)
sat = map(@hash[28, 2].to_i(16), 0, 255, 0, 20)
lum = map(@hash[30, 2].to_i(16), 0, 255, 0, 20)
color = Color::HSL.new(hue, 65 - sat, 75 - lum)
rgb = color.to_rgb
r = (rgb.r * 255).round
g = (rgb.g * 255).round
b = (rgb.b * 255).round
@bg_color = ChunkyPNG::Color.rgb(240, 240, 240)
@sprite_color = ChunkyPNG::Color.rgb(r, g, b)
end
def generate_image
half_axis = (SPRITE_SIZE - 1) / 2
margin_size = PIXEL_SIZE / 2
png = ChunkyPNG::Image.new(420, 420, @bg_color)
i = 0 # Overall count
x = half_axis * PIXEL_SIZE
while x >= 0
y = 0
while y < SPRITE_SIZE * PIXEL_SIZE
if @hash[i].to_i(16).even?
png.rect(x + margin_size, y + margin_size, x + PIXEL_SIZE + margin_size, y + PIXEL_SIZE + margin_size,
@sprite_color, @sprite_color)
if x != half_axis * PIXEL_SIZE
x_start = (2 * half_axis * PIXEL_SIZE) - x
png.rect(x_start + margin_size, y + margin_size, x_start + PIXEL_SIZE + margin_size,
y + PIXEL_SIZE + margin_size, @sprite_color, @sprite_color)
end
end
i += 1
y += PIXEL_SIZE
end
x -= PIXEL_SIZE
end
png.to_blob(:fast_rgb)
end
# A Ruby implementation of Processing's `map` function
# https://processing.org/reference/map_.html
def map(value, v_min, v_max, d_min, d_max)
v_value = value.to_f
v_range = v_max - v_min
d_range = d_max - d_min
d_value = ((v_value - v_min) * d_range / v_range) + d_min
end
end
@lunaisnotaboy
Copy link
Author

you would use it like this:

require 'digest'

hash = Digest::MD5.hexdigest('email')

identicon = Identicon.new(hash)
icon = identicon.generate_image
icon # you could return this to a browser with the header `Content-Type: image/png` or save it to a file or something

which would produce the following image:

avatar.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment