Commit c3af0e64cc4c248d7e88d4241a0f5dfb1b43bf2d

Authored by Krzysiek Szczuka
1 parent 1ada3506
Exists in master

init

.gitignore 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +*.rbc
  2 +capybara-*.html
  3 +.rspec
  4 +/log
  5 +/tmp
  6 +/db/*.sqlite3
  7 +/public/system
  8 +/coverage/
  9 +/spec/tmp
  10 +**.orig
  11 +rerun.txt
  12 +pickle-email-*.html
  13 +
  14 +# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
  15 +config/initializers/secret_token.rb
  16 +config/secrets.yml
  17 +
  18 +## Environment normalisation:
  19 +/.bundle
  20 +/vendor/bundle
  21 +
  22 +# these should all be checked in to normalise the environment:
  23 +# Gemfile.lock, .ruby-version, .ruby-gemset
  24 +
  25 +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
  26 +.rvmrc
  27 +
  28 +# if using bower-rails ignore default bower_components path bower.json files
  29 +/vendor/assets/bower_components
  30 +*.bowerrc
  31 +bower.json
... ...
Gemfile 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +# Specify your gem's dependencies in letter_avatar.gemspec
  4 +gemspec
... ...
LICENSE.txt 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +Copyright (c) 2014 Krzysiek Szczuka
  2 +
  3 +MIT License
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining
  6 +a copy of this software and associated documentation files (the
  7 +"Software"), to deal in the Software without restriction, including
  8 +without limitation the rights to use, copy, modify, merge, publish,
  9 +distribute, sublicense, and/or sell copies of the Software, and to
  10 +permit persons to whom the Software is furnished to do so, subject to
  11 +the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be
  14 +included in all copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
README.md 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +# LetterAvatar
  2 +
  3 +TODO: Write a gem description
  4 +
  5 +## Installation
  6 +
  7 +Add this line to your application's Gemfile:
  8 +
  9 + gem 'letter_avatar'
  10 +
  11 +And then execute:
  12 +
  13 + $ bundle
  14 +
  15 +Or install it yourself as:
  16 +
  17 + $ gem install letter_avatar
  18 +
  19 +## Usage
  20 +
  21 +TODO: Write usage instructions here
  22 +
  23 +## Contributing
  24 +
  25 +1. Fork it
  26 +2. Create your feature branch (`git checkout -b my-new-feature`)
  27 +3. Commit your changes (`git commit -am 'Add some feature'`)
  28 +4. Push to the branch (`git push origin my-new-feature`)
  29 +5. Create new Pull Request
... ...
Rakefile 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require "bundler/gem_tasks"
... ...
letter_avatar.gemspec 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +# coding: utf-8
  2 +lib = File.expand_path('../lib', __FILE__)
  3 +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
  4 +require 'letter_avatar/version'
  5 +
  6 +Gem::Specification.new do |spec|
  7 + spec.name = "letter_avatar"
  8 + spec.version = LetterAvatar::VERSION
  9 + spec.authors = ["Krzysiek Szczuka"]
  10 + spec.email = ["kszczuka@gmail.com"]
  11 + spec.description = %q{TODO: Write a gem description}
  12 + spec.summary = %q{TODO: Write a gem summary}
  13 + spec.homepage = ""
  14 + spec.license = "MIT"
  15 +
  16 + spec.rubyforge_project = "letter_avatar"
  17 +
  18 + spec.files = `git ls-files`.split($/)
  19 + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  20 + spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
  21 + spec.require_paths = ["lib"]
  22 +
  23 + spec.add_development_dependency "bundler", "~> 1.3"
  24 + spec.add_development_dependency "rake"
  25 + spec.add_development_dependency "image_optim"
  26 +end
... ...
lib/letter_avatar.rb 0 → 100644
... ... @@ -0,0 +1,260 @@
  1 +require "letter_avatar/version"
  2 +require "letter_avatar/avatar"
  3 +
  4 +module LetterAvatar
  5 +
  6 + mattr_accessor :cache_path
  7 + @@cache_path = ''
  8 +
  9 + def self.setup(&block)
  10 + yield(self)
  11 + end
  12 +
  13 + def self.resize(from, to, width, height)
  14 + # NOTE: ORDER is important!
  15 + instructions = %W{
  16 + #{from}
  17 + -background transparent
  18 + -gravity center
  19 + -thumbnail #{width}x#{height}^
  20 + -extent #{width}x#{height}
  21 + -interpolate bicubic
  22 + -unsharp 2x0.5+0.7+0
  23 + -quality 98
  24 + #{to}
  25 + }.join(" ")
  26 +
  27 + `convert #{instructions}`
  28 +
  29 + if $?.exitstatus == 0
  30 + ImageOptim.new.optimize_image(to) rescue nil
  31 + true
  32 + else
  33 + false
  34 + end
  35 + end
  36 +
  37 + # palette of optimally disctinct colors
  38 + # cf. http://tools.medialab.sciences-po.fr/iwanthue/index.php
  39 + # parameters used:
  40 + # - H: 0 - 360
  41 + # - C: 0 - 2
  42 + # - L: 0.75 - 1.5
  43 + COLORS = [[198,125,40],
  44 + [61,155,243],
  45 + [74,243,75],
  46 + [238,89,166],
  47 + [52,240,224],
  48 + [177,156,155],
  49 + [240,120,145],
  50 + [111,154,78],
  51 + [237,179,245],
  52 + [237,101,95],
  53 + [89,239,155],
  54 + [43,254,70],
  55 + [163,212,245],
  56 + [65,152,142],
  57 + [165,135,246],
  58 + [181,166,38],
  59 + [187,229,206],
  60 + [77,164,25],
  61 + [179,246,101],
  62 + [234,93,37],
  63 + [225,155,115],
  64 + [142,140,188],
  65 + [223,120,140],
  66 + [249,174,27],
  67 + [244,117,225],
  68 + [137,141,102],
  69 + [75,191,146],
  70 + [188,239,142],
  71 + [164,199,145],
  72 + [173,120,149],
  73 + [59,195,89],
  74 + [222,198,220],
  75 + [68,145,187],
  76 + [236,204,179],
  77 + [159,195,72],
  78 + [188,121,189],
  79 + [166,160,85],
  80 + [181,233,37],
  81 + [236,177,85],
  82 + [121,147,160],
  83 + [234,218,110],
  84 + [241,157,191],
  85 + [62,200,234],
  86 + [133,243,34],
  87 + [88,149,110],
  88 + [59,228,248],
  89 + [183,119,118],
  90 + [251,195,45],
  91 + [113,196,122],
  92 + [197,115,70],
  93 + [80,175,187],
  94 + [103,231,238],
  95 + [240,72,133],
  96 + [228,149,241],
  97 + [180,188,159],
  98 + [172,132,85],
  99 + [180,135,251],
  100 + [236,194,58],
  101 + [217,176,109],
  102 + [88,244,199],
  103 + [186,157,239],
  104 + [113,230,96],
  105 + [206,115,165],
  106 + [244,178,163],
  107 + [230,139,26],
  108 + [241,125,89],
  109 + [83,160,66],
  110 + [107,190,166],
  111 + [197,161,210],
  112 + [198,203,245],
  113 + [238,117,19],
  114 + [228,119,116],
  115 + [131,156,41],
  116 + [145,178,168],
  117 + [139,170,220],
  118 + [233,95,125],
  119 + [87,178,230],
  120 + [157,200,119],
  121 + [237,140,76],
  122 + [229,185,186],
  123 + [144,206,212],
  124 + [236,209,158],
  125 + [185,189,79],
  126 + [34,208,66],
  127 + [84,238,129],
  128 + [133,140,134],
  129 + [67,157,94],
  130 + [168,179,25],
  131 + [140,145,240],
  132 + [151,241,125],
  133 + [67,162,107],
  134 + [200,156,21],
  135 + [169,173,189],
  136 + [226,116,189],
  137 + [133,231,191],
  138 + [194,161,63],
  139 + [241,77,99],
  140 + [241,217,53],
  141 + [123,204,105],
  142 + [210,201,119],
  143 + [229,108,155],
  144 + [240,91,72],
  145 + [187,115,210],
  146 + [240,163,100],
  147 + [178,217,57],
  148 + [179,135,116],
  149 + [204,211,24],
  150 + [186,135,57],
  151 + [223,176,135],
  152 + [204,148,151],
  153 + [116,223,50],
  154 + [95,195,46],
  155 + [123,160,236],
  156 + [181,172,131],
  157 + [142,220,202],
  158 + [240,140,112],
  159 + [172,145,164],
  160 + [228,124,45],
  161 + [135,151,243],
  162 + [42,205,125],
  163 + [192,233,116],
  164 + [119,170,114],
  165 + [158,138,26],
  166 + [73,190,183],
  167 + [185,229,243],
  168 + [227,107,55],
  169 + [196,205,202],
  170 + [132,143,60],
  171 + [233,192,237],
  172 + [62,150,220],
  173 + [205,201,141],
  174 + [106,140,190],
  175 + [161,131,205],
  176 + [135,134,158],
  177 + [198,139,81],
  178 + [115,171,32],
  179 + [101,181,67],
  180 + [149,137,119],
  181 + [37,142,183],
  182 + [183,130,175],
  183 + [168,125,133],
  184 + [124,142,87],
  185 + [236,156,171],
  186 + [232,194,91],
  187 + [219,200,69],
  188 + [144,219,34],
  189 + [219,95,187],
  190 + [145,154,217],
  191 + [165,185,100],
  192 + [127,238,163],
  193 + [224,178,198],
  194 + [119,153,120],
  195 + [124,212,92],
  196 + [172,161,105],
  197 + [231,155,135],
  198 + [157,132,101],
  199 + [122,185,146],
  200 + [53,166,51],
  201 + [70,163,90],
  202 + [150,190,213],
  203 + [210,107,60],
  204 + [166,152,185],
  205 + [159,194,159],
  206 + [39,141,222],
  207 + [202,176,161],
  208 + [95,140,229],
  209 + [168,142,87],
  210 + [93,170,203],
  211 + [159,142,54],
  212 + [14,168,39],
  213 + [94,150,149],
  214 + [187,206,136],
  215 + [157,224,166],
  216 + [235,158,208],
  217 + [109,232,216],
  218 + [141,201,87],
  219 + [208,124,118],
  220 + [142,125,214],
  221 + [19,237,174],
  222 + [72,219,41],
  223 + [234,102,111],
  224 + [168,142,79],
  225 + [188,135,35],
  226 + [95,155,143],
  227 + [148,173,116],
  228 + [223,112,95],
  229 + [228,128,236],
  230 + [206,114,54],
  231 + [195,119,88],
  232 + [235,140,94],
  233 + [235,202,125],
  234 + [233,155,153],
  235 + [214,214,238],
  236 + [246,200,35],
  237 + [151,125,171],
  238 + [132,145,172],
  239 + [131,142,118],
  240 + [199,126,150],
  241 + [61,162,123],
  242 + [58,176,151],
  243 + [215,141,69],
  244 + [225,154,220],
  245 + [220,77,167],
  246 + [233,161,64],
  247 + [130,221,137],
  248 + [81,191,129],
  249 + [169,162,140],
  250 + [174,177,222],
  251 + [236,174,47],
  252 + [233,188,180],
  253 + [69,222,172],
  254 + [71,232,93],
  255 + [118,211,238],
  256 + [157,224,83],
  257 + [218,105,73],
  258 + [126,169,36]]
  259 +
  260 +end
... ...
lib/letter_avatar/avatar.rb 0 → 100644
... ... @@ -0,0 +1,100 @@
  1 +module LetterAvatar
  2 + class Avatar
  3 +
  4 + # BUMP UP if avatar algorithm changes
  5 + VERSION = 2
  6 +
  7 + # Largest avatar generated, one day when pixel ratio hit 3
  8 + # we will need to change this
  9 + FULLSIZE = 240
  10 +
  11 + class<<self
  12 +
  13 + class Identity
  14 + attr_accessor :color, :letter
  15 +
  16 + def self.from_username(username)
  17 + identity = new
  18 + identity.color = LetterAvatar::COLORS[
  19 + Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length
  20 + ]
  21 + identity.letter = username[0].upcase
  22 +
  23 + identity
  24 + end
  25 + end
  26 +
  27 +
  28 + def cache_path
  29 + LetterAvatar.cache_path || "public/system/letter_avatars/#{VERSION}"
  30 + end
  31 +
  32 + def generate(username, size, opts = nil)
  33 + identity = Identity.from_username(username)
  34 +
  35 + cache = true
  36 + cache = false if opts && opts[:cache] == false
  37 +
  38 + size = FULLSIZE if size > FULLSIZE
  39 + filename = cached_path(identity, size)
  40 +
  41 + return filename if cache && File.exists?(filename)
  42 +
  43 + fullsize = fullsize_path(identity)
  44 + generate_fullsize(identity) if !cache || !File.exists?(fullsize)
  45 +
  46 + OptimizedImage.resize(fullsize, filename, size, size)
  47 + filename
  48 + end
  49 +
  50 + def cached_path(identity, size)
  51 + dir = "#{cache_path}/#{identity.letter}/#{identity.color.join("_")}"
  52 + FileUtils.mkdir_p(dir)
  53 +
  54 + "#{dir}/#{size}.png"
  55 + end
  56 +
  57 + def fullsize_path(identity)
  58 + cached_path(identity, FULLSIZE)
  59 + end
  60 +
  61 + def generate_fullsize(identity)
  62 + color = identity.color
  63 + letter = identity.letter
  64 +
  65 + filename = fullsize_path(identity)
  66 + stroke = darken(color, 0.8)
  67 +
  68 + instructions = %W{
  69 + -size 240x240
  70 + xc:#{to_rgb(color)}
  71 + -pointsize 200
  72 + -fill white
  73 + -gravity Center
  74 + -font 'Helvetica'
  75 + -stroke #{to_rgb(stroke)}
  76 + -strokewidth 2
  77 + -annotate -5+25 '#{letter}'
  78 + '#{filename}'
  79 + }
  80 +
  81 + `convert #{instructions.join(" ")}`
  82 +
  83 + ImageOptim.new.optimize_image(filename) rescue nil
  84 +
  85 + filename
  86 + end
  87 +
  88 + def darken(color,pct)
  89 + color.map do |n|
  90 + (n.to_f * pct).to_i
  91 + end
  92 + end
  93 +
  94 + def to_rgb(color)
  95 + r,g,b = color
  96 + "'rgb(#{r},#{g},#{b})'"
  97 + end
  98 + end
  99 + end
  100 +end
... ...
lib/letter_avatar/version.rb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +module LetterAvatar
  2 + VERSION = "0.0.1"
  3 +end
... ...