mirror of
https://github.com/discourse/discourse.git
synced 2024-12-05 10:15:47 +08:00
c5fd8c42db
* File.exists? is deprecated and removed in Ruby 3.2 in favor of File.exist? * Dir.exists? is deprecated and removed in Ruby 3.2 in favor of Dir.exist?
347 lines
8.0 KiB
Ruby
347 lines
8.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class LetterAvatar
|
|
|
|
class Identity
|
|
attr_accessor :color, :letter
|
|
|
|
def self.from_username(username)
|
|
identity = new
|
|
identity.color = LetterAvatar::COLORS[
|
|
Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length
|
|
]
|
|
identity.letter = username[0].upcase
|
|
identity
|
|
end
|
|
end
|
|
|
|
# BUMP UP if avatar algorithm changes
|
|
VERSION = 5
|
|
|
|
# CHANGE these values to support more pixel ratios
|
|
FULLSIZE = 120 * 3
|
|
POINTSIZE = 280
|
|
|
|
class << self
|
|
|
|
def version
|
|
"#{VERSION}_#{image_magick_version}"
|
|
end
|
|
|
|
def cache_path
|
|
"tmp/letter_avatars/#{version}"
|
|
end
|
|
|
|
def generate(username, size, opts = nil)
|
|
DistributedMutex.synchronize("letter_avatar_#{version}_#{username}") do
|
|
identity = (opts && opts[:identity]) || LetterAvatar::Identity.from_username(username)
|
|
|
|
cache = true
|
|
cache = false if opts && opts[:cache] == false
|
|
|
|
size = FULLSIZE if size > FULLSIZE
|
|
filename = cached_path(identity, size)
|
|
|
|
return filename if cache && File.exist?(filename)
|
|
|
|
fullsize = fullsize_path(identity)
|
|
generate_fullsize(identity) if !cache || !File.exist?(fullsize)
|
|
|
|
# Optimizing here is dubious, it can save up to 2x for large images (eg 359px)
|
|
# BUT... we are talking 2400 bytes down to 1200 bytes, both fit in one packet
|
|
# The cost of this is huge, its a 40% perf hit
|
|
OptimizedImage.resize(fullsize, filename, size, size)
|
|
|
|
filename
|
|
end
|
|
end
|
|
|
|
def cached_path(identity, size)
|
|
dir = "#{cache_path}/#{identity.letter}/#{identity.color.join("_")}"
|
|
FileUtils.mkdir_p(dir)
|
|
File.expand_path "#{dir}/#{size}.png"
|
|
end
|
|
|
|
def fullsize_path(identity)
|
|
File.expand_path cached_path(identity, FULLSIZE)
|
|
end
|
|
|
|
def generate_fullsize(identity)
|
|
color = identity.color
|
|
letter = identity.letter
|
|
|
|
filename = fullsize_path(identity)
|
|
|
|
instructions = %W{
|
|
-size #{FULLSIZE}x#{FULLSIZE}
|
|
xc:#{to_rgb(color)}
|
|
-pointsize #{POINTSIZE}
|
|
-fill #FFFFFFCC
|
|
-font Helvetica
|
|
-gravity Center
|
|
-annotate -0+26 #{letter}
|
|
-depth 8
|
|
#{filename}
|
|
}
|
|
|
|
Discourse::Utils.execute_command('convert', *instructions)
|
|
|
|
## do not optimize image, it will end up larger than original
|
|
filename
|
|
end
|
|
|
|
def to_rgb(color)
|
|
r, g, b = color
|
|
"rgb(#{r},#{g},#{b})"
|
|
end
|
|
|
|
def image_magick_version
|
|
@image_magick_version ||=
|
|
begin
|
|
Thread.new do
|
|
sleep 2
|
|
cleanup_old
|
|
end
|
|
Digest::MD5.hexdigest(`convert --version` << `convert -list font`)
|
|
end
|
|
end
|
|
|
|
def cleanup_old
|
|
begin
|
|
skip = File.basename(cache_path)
|
|
parent_path = File.dirname(cache_path)
|
|
Dir.entries(parent_path).each do |path|
|
|
unless ['.', '..'].include?(path) || path == skip
|
|
FileUtils.rm_rf(parent_path + "/" + path)
|
|
end
|
|
end
|
|
rescue Errno::ENOENT
|
|
# no worries, folder doesn't exists
|
|
end
|
|
end
|
|
end
|
|
|
|
# palette of optimally distinct colors
|
|
# cf. http://tools.medialab.sciences-po.fr/iwanthue/index.php
|
|
# parameters used:
|
|
# - H: 0 - 360
|
|
# - C: 0 - 2
|
|
# - L: 0.75 - 1.5
|
|
COLORS = [[198, 125, 40],
|
|
[61, 155, 243],
|
|
[74, 243, 75],
|
|
[238, 89, 166],
|
|
[52, 240, 224],
|
|
[177, 156, 155],
|
|
[240, 120, 145],
|
|
[111, 154, 78],
|
|
[237, 179, 245],
|
|
[237, 101, 95],
|
|
[89, 239, 155],
|
|
[43, 254, 70],
|
|
[163, 212, 245],
|
|
[65, 152, 142],
|
|
[165, 135, 246],
|
|
[181, 166, 38],
|
|
[187, 229, 206],
|
|
[77, 164, 25],
|
|
[179, 246, 101],
|
|
[234, 93, 37],
|
|
[225, 155, 115],
|
|
[142, 140, 188],
|
|
[223, 120, 140],
|
|
[249, 174, 27],
|
|
[244, 117, 225],
|
|
[137, 141, 102],
|
|
[75, 191, 146],
|
|
[188, 239, 142],
|
|
[164, 199, 145],
|
|
[173, 120, 149],
|
|
[59, 195, 89],
|
|
[222, 198, 220],
|
|
[68, 145, 187],
|
|
[236, 204, 179],
|
|
[159, 195, 72],
|
|
[188, 121, 189],
|
|
[166, 160, 85],
|
|
[181, 233, 37],
|
|
[236, 177, 85],
|
|
[121, 147, 160],
|
|
[234, 218, 110],
|
|
[241, 157, 191],
|
|
[62, 200, 234],
|
|
[133, 243, 34],
|
|
[88, 149, 110],
|
|
[59, 228, 248],
|
|
[183, 119, 118],
|
|
[251, 195, 45],
|
|
[113, 196, 122],
|
|
[197, 115, 70],
|
|
[80, 175, 187],
|
|
[103, 231, 238],
|
|
[240, 72, 133],
|
|
[228, 149, 241],
|
|
[180, 188, 159],
|
|
[172, 132, 85],
|
|
[180, 135, 251],
|
|
[236, 194, 58],
|
|
[217, 176, 109],
|
|
[88, 244, 199],
|
|
[186, 157, 239],
|
|
[113, 230, 96],
|
|
[206, 115, 165],
|
|
[244, 178, 163],
|
|
[230, 139, 26],
|
|
[241, 125, 89],
|
|
[83, 160, 66],
|
|
[107, 190, 166],
|
|
[197, 161, 210],
|
|
[198, 203, 245],
|
|
[238, 117, 19],
|
|
[228, 119, 116],
|
|
[131, 156, 41],
|
|
[145, 178, 168],
|
|
[139, 170, 220],
|
|
[233, 95, 125],
|
|
[87, 178, 230],
|
|
[157, 200, 119],
|
|
[237, 140, 76],
|
|
[229, 185, 186],
|
|
[144, 206, 212],
|
|
[236, 209, 158],
|
|
[185, 189, 79],
|
|
[34, 208, 66],
|
|
[84, 238, 129],
|
|
[133, 140, 134],
|
|
[67, 157, 94],
|
|
[168, 179, 25],
|
|
[140, 145, 240],
|
|
[151, 241, 125],
|
|
[67, 162, 107],
|
|
[200, 156, 21],
|
|
[169, 173, 189],
|
|
[226, 116, 189],
|
|
[133, 231, 191],
|
|
[194, 161, 63],
|
|
[241, 77, 99],
|
|
[241, 217, 53],
|
|
[123, 204, 105],
|
|
[210, 201, 119],
|
|
[229, 108, 155],
|
|
[240, 91, 72],
|
|
[187, 115, 210],
|
|
[240, 163, 100],
|
|
[178, 217, 57],
|
|
[179, 135, 116],
|
|
[204, 211, 24],
|
|
[186, 135, 57],
|
|
[223, 176, 135],
|
|
[204, 148, 151],
|
|
[116, 223, 50],
|
|
[95, 195, 46],
|
|
[123, 160, 236],
|
|
[181, 172, 131],
|
|
[142, 220, 202],
|
|
[240, 140, 112],
|
|
[172, 145, 164],
|
|
[228, 124, 45],
|
|
[135, 151, 243],
|
|
[42, 205, 125],
|
|
[192, 233, 116],
|
|
[119, 170, 114],
|
|
[158, 138, 26],
|
|
[73, 190, 183],
|
|
[185, 229, 243],
|
|
[227, 107, 55],
|
|
[196, 205, 202],
|
|
[132, 143, 60],
|
|
[233, 192, 237],
|
|
[62, 150, 220],
|
|
[205, 201, 141],
|
|
[106, 140, 190],
|
|
[161, 131, 205],
|
|
[135, 134, 158],
|
|
[198, 139, 81],
|
|
[115, 171, 32],
|
|
[101, 181, 67],
|
|
[149, 137, 119],
|
|
[37, 142, 183],
|
|
[183, 130, 175],
|
|
[168, 125, 133],
|
|
[124, 142, 87],
|
|
[236, 156, 171],
|
|
[232, 194, 91],
|
|
[219, 200, 69],
|
|
[144, 219, 34],
|
|
[219, 95, 187],
|
|
[145, 154, 217],
|
|
[165, 185, 100],
|
|
[127, 238, 163],
|
|
[224, 178, 198],
|
|
[119, 153, 120],
|
|
[124, 212, 92],
|
|
[172, 161, 105],
|
|
[231, 155, 135],
|
|
[157, 132, 101],
|
|
[122, 185, 146],
|
|
[53, 166, 51],
|
|
[70, 163, 90],
|
|
[150, 190, 213],
|
|
[210, 107, 60],
|
|
[166, 152, 185],
|
|
[159, 194, 159],
|
|
[39, 141, 222],
|
|
[202, 176, 161],
|
|
[95, 140, 229],
|
|
[168, 142, 87],
|
|
[93, 170, 203],
|
|
[159, 142, 54],
|
|
[14, 168, 39],
|
|
[94, 150, 149],
|
|
[187, 206, 136],
|
|
[157, 224, 166],
|
|
[235, 158, 208],
|
|
[109, 232, 216],
|
|
[141, 201, 87],
|
|
[208, 124, 118],
|
|
[142, 125, 214],
|
|
[19, 237, 174],
|
|
[72, 219, 41],
|
|
[234, 102, 111],
|
|
[168, 142, 79],
|
|
[188, 135, 35],
|
|
[95, 155, 143],
|
|
[148, 173, 116],
|
|
[223, 112, 95],
|
|
[228, 128, 236],
|
|
[206, 114, 54],
|
|
[195, 119, 88],
|
|
[235, 140, 94],
|
|
[235, 202, 125],
|
|
[233, 155, 153],
|
|
[214, 214, 238],
|
|
[246, 200, 35],
|
|
[151, 125, 171],
|
|
[132, 145, 172],
|
|
[131, 142, 118],
|
|
[199, 126, 150],
|
|
[61, 162, 123],
|
|
[58, 176, 151],
|
|
[215, 141, 69],
|
|
[225, 154, 220],
|
|
[220, 77, 167],
|
|
[233, 161, 64],
|
|
[130, 221, 137],
|
|
[81, 191, 129],
|
|
[169, 162, 140],
|
|
[174, 177, 222],
|
|
[236, 174, 47],
|
|
[233, 188, 180],
|
|
[69, 222, 172],
|
|
[71, 232, 93],
|
|
[118, 211, 238],
|
|
[157, 224, 83],
|
|
[218, 105, 73],
|
|
[126, 169, 36]]
|
|
end
|