scarf 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/scarf/identicon.rb +177 -0
- data/lib/scarf/image_svg.rb +60 -0
- data/lib/scarf/initial_avatar.rb +45 -0
- data/lib/scarf/version.rb +3 -0
- data/lib/scarf.rb +4 -0
- data/test/test_helper.rb +5 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4026733a01ae99c008ae69f75a4135e8cd7826a1
|
4
|
+
data.tar.gz: 1c4f9658d3e320298f623b5448b494f878928e7f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 27ea92c4698fc7d5bc7aa44d58027b97fd55b014c156eb50e814d2228fcf2583c13a151afa38c3359c76a25325e659396e772212d2421e21097dad1938d49a48
|
7
|
+
data.tar.gz: 2dc7c53f1c897c2a31c8aecec84a0feff774951c401b9219fe1c5a5cce68c4ea0f88c13fdb5f06dba9c8a747449aa460e8f0c65643fbfdfbee0b3dacf5655f50
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module Scarf
|
2
|
+
class Identicon
|
3
|
+
PATCHES = [
|
4
|
+
[0, 4, 24, 20, 0],
|
5
|
+
[0, 4, 20, 0],
|
6
|
+
[2, 24, 20, 2],
|
7
|
+
[0, 2, 22, 20, 0],
|
8
|
+
[2, 14, 22, 10, 2],
|
9
|
+
[0, 14, 24, 22, 0],
|
10
|
+
[2, 24, 22, 13, 11, 22, 20, 2],
|
11
|
+
[0, 14, 22, 0],
|
12
|
+
[6, 8, 18, 16, 6],
|
13
|
+
[4, 20, 10, 12, 2, 4],
|
14
|
+
[0, 2, 12, 10, 0],
|
15
|
+
[10, 14, 22, 10],
|
16
|
+
[20, 12, 24, 20],
|
17
|
+
[10, 2, 12, 10],
|
18
|
+
[0, 2, 10, 0],
|
19
|
+
[],
|
20
|
+
]
|
21
|
+
|
22
|
+
CENTER_PATCHES = [0, 4, 8, 15]
|
23
|
+
PATCH_SIZE = 5
|
24
|
+
|
25
|
+
attr_reader :code
|
26
|
+
|
27
|
+
def initialize(str = '', opt = {})
|
28
|
+
case opt[:type]
|
29
|
+
when :code
|
30
|
+
@code = str.to_i
|
31
|
+
when :ip
|
32
|
+
@code = Identicon.ip2code str
|
33
|
+
else
|
34
|
+
@code = Identicon.calc_code str.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
@decode = decode @code
|
38
|
+
|
39
|
+
if opt[:size]
|
40
|
+
@scale = opt[:size].to_f / (PATCH_SIZE * 3)
|
41
|
+
else
|
42
|
+
@scale = opt[:scale] || 1
|
43
|
+
end
|
44
|
+
|
45
|
+
@image_lib = ImageSVG
|
46
|
+
|
47
|
+
@transparent = !!opt[:transparent] &&
|
48
|
+
[ImageRmagick, ImageSVG].include?(@image_lib)
|
49
|
+
@patch_width = PATCH_SIZE * @scale
|
50
|
+
@image = @image_lib.new(@patch_width * 3, @patch_width * 3,
|
51
|
+
:transparent => @transparent)
|
52
|
+
@back_color = @image.color 255, 255, 255
|
53
|
+
@fore_color = opt[:color] || @image.color(@decode[:red], @decode[:green], @decode[:blue])
|
54
|
+
render
|
55
|
+
end
|
56
|
+
|
57
|
+
def decode(code)
|
58
|
+
{
|
59
|
+
:center_type => (code & 0x3),
|
60
|
+
:center_invert => (((code >> 2) & 0x01) != 0),
|
61
|
+
:corner_type => ((code >> 3) & 0x0f),
|
62
|
+
:corner_invert => (((code >> 7) & 0x01) != 0),
|
63
|
+
:corner_turn => ((code >> 8) & 0x03),
|
64
|
+
:side_type => ((code >> 10) & 0x0f),
|
65
|
+
:side_invert => (((code >> 14) & 0x01) != 0),
|
66
|
+
:side_turn => ((code >> 15) & 0x03),
|
67
|
+
:red => (((code >> 16) & 0x01f) << 3),
|
68
|
+
:green => (((code >> 21) & 0x01f) << 3),
|
69
|
+
:blue => (((code >> 27) & 0x01f) << 3),
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def render
|
74
|
+
center = [[1, 1]]
|
75
|
+
side = [[1, 0], [2, 1], [1, 2], [0, 1]]
|
76
|
+
corner = [[0, 0], [2, 0], [2, 2], [0, 2]]
|
77
|
+
|
78
|
+
draw_patches(center, CENTER_PATCHES[@decode[:center_type]],
|
79
|
+
0, @decode[:center_invert])
|
80
|
+
draw_patches(side, @decode[:side_type],
|
81
|
+
@decode[:side_turn], @decode[:side_invert])
|
82
|
+
draw_patches(corner, @decode[:corner_type],
|
83
|
+
@decode[:corner_turn], @decode[:corner_invert])
|
84
|
+
end
|
85
|
+
|
86
|
+
def draw_patches(list, patch, turn, invert)
|
87
|
+
list.each do |i|
|
88
|
+
draw(:x => i[0], :y => i[1], :patch => patch,
|
89
|
+
:turn => turn, :invert => invert)
|
90
|
+
turn += 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def draw(opt = {})
|
95
|
+
x = opt[:x] * @patch_width
|
96
|
+
y = opt[:y] * @patch_width
|
97
|
+
patch = opt[:patch] % PATCHES.size
|
98
|
+
turn = opt[:turn] % 4
|
99
|
+
|
100
|
+
if opt[:invert]
|
101
|
+
fore, back = @back_color, @fore_color
|
102
|
+
else
|
103
|
+
fore, back = @fore_color, @back_color
|
104
|
+
end
|
105
|
+
|
106
|
+
offset = (@image_lib == Scarf::ImageSVG) ? 0 : 1
|
107
|
+
|
108
|
+
if !(@transparent && back == @back_color)
|
109
|
+
@image.fill_rect(x, y, x + @patch_width - offset,
|
110
|
+
y + @patch_width - offset, back)
|
111
|
+
end
|
112
|
+
|
113
|
+
points = []
|
114
|
+
PATCHES[patch].each do |pt|
|
115
|
+
dx = pt % PATCH_SIZE
|
116
|
+
dy = pt / PATCH_SIZE
|
117
|
+
len = @patch_width - offset
|
118
|
+
px = dx.to_f / (PATCH_SIZE - 1) * len
|
119
|
+
py = dy.to_f / (PATCH_SIZE - 1) * len
|
120
|
+
|
121
|
+
case turn
|
122
|
+
when 1
|
123
|
+
px, py = len - py, px
|
124
|
+
when 2
|
125
|
+
px, py = len - px, len - py
|
126
|
+
when 3
|
127
|
+
px, py = py, len - px
|
128
|
+
end
|
129
|
+
points << [x + px, y + py]
|
130
|
+
end
|
131
|
+
|
132
|
+
if @transparent
|
133
|
+
if fore == @back_color
|
134
|
+
@image.polygon_clip points
|
135
|
+
else
|
136
|
+
@image.polygon points, fore
|
137
|
+
end
|
138
|
+
else
|
139
|
+
@image.polygon points, fore
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def write(path = "#{@code}.png")
|
144
|
+
@image.write(path)
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_blob
|
148
|
+
@image.to_blob
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.calc_code(str)
|
152
|
+
extract_code(Identicon.digest(str))
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.ip2code(ip)
|
156
|
+
code_ip = extract_code(ip.split('.'))
|
157
|
+
extract_code(Identicon.digest(code_ip.to_s))
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.digest(str)
|
161
|
+
Digest::SHA1.digest(str)
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.extract_code(list)
|
165
|
+
if list.respond_to?(:getbyte)
|
166
|
+
tmp = [list.getbyte(0) << 24, list.getbyte(1) << 16,
|
167
|
+
list.getbyte(2) << 8, list.getbyte(3)]
|
168
|
+
else
|
169
|
+
tmp = [list[0].to_i << 24, list[1].to_i << 16,
|
170
|
+
list[2].to_i << 8, list[3].to_i]
|
171
|
+
end
|
172
|
+
tmp.inject(0) do |r, i|
|
173
|
+
r | ((i[31] == 1) ? -(i & 0x7fffffff) : i)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Scarf
|
2
|
+
class ImageSVG
|
3
|
+
def initialize(width, height, opt = {})
|
4
|
+
@transparent = !!opt[:transparent]
|
5
|
+
@mask = @transparent ? 'mask="clip"' : ''
|
6
|
+
@masks = ''
|
7
|
+
@image = ''
|
8
|
+
@width = width
|
9
|
+
@height = height
|
10
|
+
@head = %(<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 #{width} #{height}">\n)
|
11
|
+
end
|
12
|
+
|
13
|
+
def color(r, g, b)
|
14
|
+
'rgb(%d, %d, %d)' % [r, g, b]
|
15
|
+
end
|
16
|
+
|
17
|
+
def fill_rect(x, y, _x, _y, color)
|
18
|
+
@image << %(<rect x="#{x}" y="#{y}" width="#{_x - x}" height="#{_y - y}" fill="#{color}" stroke-width="0" stroke="white" mask="url(#clip)"/>\n)
|
19
|
+
end
|
20
|
+
|
21
|
+
def polygon(points, color)
|
22
|
+
unless points.empty?
|
23
|
+
ps = points.map { |i| i.join(',') }.join(' ')
|
24
|
+
@image << %(<polygon points="#{ps}" stroke-width="0" fill="#{color}" mask="(#clip)"/>\n)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def polygon_clip(points)
|
29
|
+
unless points.empty?
|
30
|
+
ps = points.map { |i| i.join(',') }.join(' ')
|
31
|
+
@masks << %(<polygon points="#{ps}" stroke-width="0"/>\n)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def write(path)
|
36
|
+
open(path, 'wb') { |f| f.write self.to_blob }
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_blob
|
40
|
+
if @transparent
|
41
|
+
@head + build_mask + @image + "\n</svg>"
|
42
|
+
else
|
43
|
+
@head + @image + "\n</svg>"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def build_mask
|
50
|
+
<<~MASK
|
51
|
+
<mask id="clip" maskUnits="userSpaceOnUse" x="0" y="0" width="#{@width}" height="#{@height}">
|
52
|
+
<g fill="black" fill-rule="evenodd">
|
53
|
+
<rect x="0" y="0" width="#{@width}" height="#{@height}" fill="white"/>
|
54
|
+
#{@masks}
|
55
|
+
</g>
|
56
|
+
</mask>
|
57
|
+
MASK
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Scarf
|
2
|
+
class InitialAvatar
|
3
|
+
def initialize(name, font_family: [])
|
4
|
+
@name = name
|
5
|
+
@font_family = font_family
|
6
|
+
end
|
7
|
+
|
8
|
+
BACKGROUND = %w(#ff4040 #7f2020 #cc5c33 #734939 #bf9c8f #995200 #4c2900 #f2a200 #ffd580 #332b1a #4c3d00 #ffee00 #b0b386 #64664d #6c8020 #c3d96c #143300 #19bf00 #53a669 #bfffd9 #40ffbf #1a332e #00b3a7 #165955 #00b8e6 #69818c #005ce6 #6086bf #000e66 #202440 #393973 #4700b3 #2b0d33 #aa86b3 #ee00ff #bf60b9 #4d3949 #ff00aa #7f0044 #f20061 #330007 #d96c7b).freeze
|
9
|
+
|
10
|
+
def svg
|
11
|
+
<<~WRAPPER.gsub(/$\s+/, '').strip
|
12
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
|
13
|
+
<circle cx="50" cy="50" r="50" fill="#{fill}" />
|
14
|
+
<text fill="#FFFFFF" font-size="50" font-family="#{font_family}" x="50" y="50" text-anchor="middle" alignment-baseline="central">#{initials}</text>
|
15
|
+
</svg>
|
16
|
+
WRAPPER
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Default to sans-serif.
|
22
|
+
def font_family
|
23
|
+
(quoted_fonts + %w(sans-serif)).join(', ')
|
24
|
+
end
|
25
|
+
|
26
|
+
# Surround the font name with single quotes if there's a space in the name, and isn't quoted already.
|
27
|
+
def quoted_fonts
|
28
|
+
@font_family.map do |font|
|
29
|
+
return font unless font.include?(' ')
|
30
|
+
return font if font[0] == "'"
|
31
|
+
"'#{font}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: Need to add a hashing logic to ensure a supplied name always returns the same colour.
|
36
|
+
def fill
|
37
|
+
BACKGROUND.sample
|
38
|
+
end
|
39
|
+
|
40
|
+
# Upcase first letter of up to first two segments of the supplied name.
|
41
|
+
def initials
|
42
|
+
@name.split[0..1].map { |segment| segment[0] }.join('').upcase
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/scarf.rb
ADDED
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: scarf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- harigopal
|
8
|
+
- swdyh
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-04-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '12.0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '12.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: test-unit
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.1'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.1'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.5'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.5'
|
56
|
+
description: |-
|
57
|
+
A Ruby library for generating initial avatars and identicons, forked from swdyh/quilt
|
58
|
+
https://github.com/harigopal/quilt
|
59
|
+
email: mail@harigopal.in
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- lib/scarf.rb
|
65
|
+
- lib/scarf/identicon.rb
|
66
|
+
- lib/scarf/image_svg.rb
|
67
|
+
- lib/scarf/initial_avatar.rb
|
68
|
+
- lib/scarf/version.rb
|
69
|
+
- test/test_helper.rb
|
70
|
+
homepage: https://github.com/harigopal/scarf
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.3.0
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.6.11
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: A Ruby library for generating initial avatars and identicons.
|
94
|
+
test_files:
|
95
|
+
- test/test_helper.rb
|