the-architect-sprite_generator 0.1.12 → 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.
- data/lib/sprite_generator.rb +147 -85
- data/test/units/test_sprite_generator.rb +30 -1
- metadata +1 -1
data/lib/sprite_generator.rb
CHANGED
@@ -39,29 +39,24 @@ class SpriteGenerator
|
|
39
39
|
def initialize(files_or_paths, output, root, options = {})
|
40
40
|
@files = find_files(files_or_paths)
|
41
41
|
return if @files.nil? || @files.empty?
|
42
|
-
@root
|
43
|
-
@output
|
44
|
-
@delimiter
|
45
|
-
@analyzed
|
46
|
-
@template
|
47
|
-
|
48
|
-
@
|
49
|
-
@
|
50
|
-
@
|
51
|
-
@alignment = options[:alignment] ? Magick.const_get("#{camelize(options[:alignment])}Gravity") : Magick::CenterGravity
|
42
|
+
@root = root || ''
|
43
|
+
@output = output
|
44
|
+
@delimiter = options[:delimiter] || '-'
|
45
|
+
@analyzed = find_files_for_mode(options[:distribution] || :smart)
|
46
|
+
@template = Liquid::Template.parse(options[:template] || '')
|
47
|
+
@sprite_location = options[:sprite_location] || @output
|
48
|
+
@background = options[:background] || '#FFFFFF00'
|
49
|
+
@tile_size = options[:tile]
|
50
|
+
@alignment = options[:alignment] ? Magick.const_get("#{camelize(options[:alignment])}Gravity") : Magick::CenterGravity
|
52
51
|
end
|
53
52
|
|
54
53
|
|
55
54
|
def create
|
56
55
|
raise 'No files found.' if @files.nil? || @files.empty?
|
57
|
-
|
58
|
-
unless @tile_size.nil?
|
59
|
-
size_x, size_y = @tile_size.split('x').first(2).map{|dim| dim.to_i}
|
60
|
-
tile = Magick::Image.new(size_x, size_y){ self.background_color = background }
|
61
|
-
tile.format = "PNG"
|
62
|
-
end
|
56
|
+
build_tile
|
63
57
|
destination = @root.nil? || @root.empty? ? @output : File.join(@root, @output)
|
64
|
-
image, css
|
58
|
+
image, css = build_sprite_and_css
|
59
|
+
background = @background
|
65
60
|
image.write(destination){ self.background_color = background }
|
66
61
|
css
|
67
62
|
end
|
@@ -69,66 +64,99 @@ class SpriteGenerator
|
|
69
64
|
|
70
65
|
protected
|
71
66
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
def build(tile = nil)
|
78
|
-
background = @background
|
79
|
-
images = ImageList.new{ self.background_color = background }
|
80
|
-
context = { 'sprite_location' => @sprite_location, 'tile' => tile }
|
81
|
-
css = []
|
67
|
+
def build_sprite_and_css
|
68
|
+
background = @background
|
69
|
+
@images = ImageList.new{ self.background_color = background }
|
70
|
+
context = { 'sprite_location' => @sprite_location }
|
71
|
+
@css = []
|
82
72
|
|
83
|
-
@analyzed.each do |key
|
84
|
-
|
85
|
-
|
73
|
+
@analyzed.keys.sort.each do |key|
|
74
|
+
value = @analyzed[key]
|
75
|
+
if @tile
|
76
|
+
context['left'] = @images.length > 0 ? @tile.columns : 0
|
86
77
|
else
|
87
|
-
context['left'] = images.length > 0 ? images.append(false).columns : 0
|
78
|
+
context['left'] = @images.length > 0 ? @images.append(false).columns : 0
|
88
79
|
end
|
89
|
-
context['top']
|
80
|
+
context['top'] = 0
|
90
81
|
context['basename'] = key
|
91
|
-
context['overall']
|
82
|
+
context['overall'] = @images.length
|
92
83
|
|
93
|
-
|
94
|
-
|
95
|
-
if tile
|
96
|
-
tiles = ImageList.new{ self.background_color = background }
|
97
|
-
image_list.each do |image|
|
98
|
-
tiles << tile.composite(image, @alignment, Magick::OverCompositeOp)
|
99
|
-
end
|
100
|
-
images.from_blob(tiles.append(true).to_blob){ self.background_color = background }
|
101
|
-
else
|
102
|
-
images.from_blob(image_list.append(true).to_blob){ self.background_color = background }
|
103
|
-
end
|
104
|
-
context['variations'] = image_list.length
|
105
|
-
context['type'] = :list
|
106
|
-
context['images'] = image_list
|
107
|
-
context['filenames'] = value
|
108
|
-
else
|
109
|
-
|
110
|
-
filename = value.flatten.first
|
111
|
-
image = Image.read(filename){ self.background_color = background }
|
112
|
-
|
113
|
-
context['variations'] = 0
|
114
|
-
context['variation_name'] = ''
|
115
|
-
context['variation_number'] = 0
|
116
|
-
context['full_filename'] = filename
|
117
|
-
context['file_basename'] = File.basename(filename, '.*')
|
118
|
-
context['variation_name'] = context['file_basename'].gsub(/^#{context['basename']}#{@delimiter}/, '')
|
119
|
-
|
120
|
-
context['type'] = :image
|
121
|
-
|
122
|
-
if tile
|
123
|
-
images.from_blob(tile.composite(image.first, @alignment, Magick::OverCompositeOp).to_blob){ self.background_color = background }
|
124
|
-
else
|
125
|
-
images.from_blob(image.first.to_blob){ self.background_color = background }
|
126
|
-
end
|
127
|
-
end
|
128
|
-
css << build_css(context)
|
84
|
+
value.size > 1 ? build_image_list(context, value.sort) : build_single_image(context, value.flatten.first)
|
85
|
+
@css << build_css(context)
|
129
86
|
end
|
130
87
|
|
131
|
-
[images.append(false), css.join("\n")]
|
88
|
+
[@images.append(false), @css.join("\n")]
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def build_single_image(context, filename)
|
93
|
+
background = @background
|
94
|
+
image = Image.read(filename){ self.background_color = background }.flatten.first
|
95
|
+
|
96
|
+
context.merge!(build_context_for_single_image(image, filename, context['basename']))
|
97
|
+
|
98
|
+
if @tile
|
99
|
+
@images.from_blob(@tile.composite(image, @alignment, Magick::OverCompositeOp).to_blob){ self.background_color = background }
|
100
|
+
else
|
101
|
+
@images.from_blob(image.to_blob){ self.background_color = background }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def build_context_for_single_image(image, filename, basename)
|
107
|
+
file_basename = File.basename(filename, '.*')
|
108
|
+
{
|
109
|
+
'variations' => 0,
|
110
|
+
'variation_name' => '',
|
111
|
+
'variation_number' => 0,
|
112
|
+
'full_filename' => filename,
|
113
|
+
'file_basename' => file_basename,
|
114
|
+
'variation_name' => file_basename.gsub(/^#{basename}#{@delimiter}/, ''),
|
115
|
+
'width' => @tile ? tile.columns : image.columns,
|
116
|
+
'height' => @tile ? tile.rows : image.rows,
|
117
|
+
'type' => :image
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def build_image_list(context, image_filenames)
|
123
|
+
background = @background
|
124
|
+
image_list = ImageList.new(*image_filenames){ self.background_color = background }
|
125
|
+
if @tile
|
126
|
+
tiles = ImageList.new{ self.background_color = background }
|
127
|
+
image_list.each do |image|
|
128
|
+
tiles << @tile.composite(image, @alignment, Magick::OverCompositeOp)
|
129
|
+
end
|
130
|
+
append_to_sprite(tiles)
|
131
|
+
else
|
132
|
+
append_to_sprite(image_list)
|
133
|
+
end
|
134
|
+
context.merge!(build_context_for_image_list(image_list, image_filenames))
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def append_to_sprite(images)
|
139
|
+
background = @background
|
140
|
+
@images.from_blob(images.append(true).to_blob){ self.background_color = background }
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def build_context_for_image_list(image_list, image_filenames)
|
145
|
+
{
|
146
|
+
'variations' => image_list.length,
|
147
|
+
'type' => :list,
|
148
|
+
'images' => image_list,
|
149
|
+
'filenames' => image_filenames
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
def build_tile
|
155
|
+
return if @tile_size.nil?
|
156
|
+
background = @background
|
157
|
+
size_x, size_y = @tile_size.split('x').first(2).map{|dim| dim.to_i}
|
158
|
+
@tile = Magick::Image.new(size_x, size_y){ self.background_color = background }
|
159
|
+
@tile.format = "PNG"
|
132
160
|
end
|
133
161
|
|
134
162
|
|
@@ -147,21 +175,19 @@ protected
|
|
147
175
|
|
148
176
|
def build_css_for_list(context)
|
149
177
|
new_context = context.dup
|
150
|
-
|
151
|
-
image_list = context.delete('images')
|
178
|
+
image_list = context.delete('images')
|
152
179
|
new_context['type'] = :image
|
153
180
|
css = image_list.inject([]) do |css, image|
|
154
|
-
new_context['width']
|
155
|
-
new_context['height']
|
181
|
+
new_context['width'] = @tile ? @tile.columns : image.columns
|
182
|
+
new_context['height'] = @tile ? @tile.rows : image.rows
|
156
183
|
new_context['variation_number'] = css.size
|
157
|
-
|
158
|
-
new_context['
|
159
|
-
new_context['
|
160
|
-
new_context['
|
161
|
-
new_context['variation_name'] = new_context['file_basename'].gsub(/^#{new_context['basename']}#{@delimiter}/, '')
|
184
|
+
new_context['full_filename'] = context['filenames'].shift
|
185
|
+
new_context['filename'] = File.basename(new_context['full_filename'])
|
186
|
+
new_context['file_basename'] = File.basename(new_context['full_filename'], '.*')
|
187
|
+
new_context['variation_name'] = new_context['file_basename'].gsub(/^#{new_context['basename']}#{@delimiter}/, '')
|
162
188
|
|
163
189
|
css << build_css(new_context.dup)
|
164
|
-
new_context['top'] += tile ? tile.columns : new_context['height']
|
190
|
+
new_context['top'] += @tile ? @tile.columns : new_context['height']
|
165
191
|
css
|
166
192
|
end.join("\n")
|
167
193
|
end
|
@@ -181,17 +207,53 @@ protected
|
|
181
207
|
end
|
182
208
|
|
183
209
|
|
210
|
+
# put filenames in format for each mode
|
211
|
+
def find_files_for_mode(mode)
|
212
|
+
case mode
|
213
|
+
when :smart
|
214
|
+
smart_distribution
|
215
|
+
when :horizontal
|
216
|
+
horizontal_distribution
|
217
|
+
when :vertical
|
218
|
+
vertical_distribution
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
184
223
|
# gather information about the selected files
|
185
224
|
# check for variations by using a delimiter as an indicator
|
186
|
-
def
|
187
|
-
|
188
|
-
basename
|
189
|
-
without_variation = basename.split(delimiter)[0..-2].join(delimiter)
|
190
|
-
basename
|
225
|
+
def smart_distribution
|
226
|
+
@files.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
|
227
|
+
basename = File.basename(file, '.*')
|
228
|
+
without_variation = basename.split(@delimiter)[0..-2].join(@delimiter)
|
229
|
+
basename = without_variation.nil? || without_variation == '' ? basename : without_variation
|
191
230
|
h[basename] << file
|
192
231
|
h
|
193
232
|
end
|
194
233
|
end
|
195
234
|
|
196
235
|
|
236
|
+
def horizontal_distribution
|
237
|
+
@files.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
|
238
|
+
basename = File.basename(file)
|
239
|
+
h[basename] << file
|
240
|
+
h
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
def vertical_distribution
|
246
|
+
@files.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
|
247
|
+
basename = File.basename(file)
|
248
|
+
h['all'] << file
|
249
|
+
h
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
# simplyfied version of active_support's camelize version
|
255
|
+
def camelize(lower_case_and_underscored_word)
|
256
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
257
|
+
end
|
258
|
+
|
197
259
|
end
|
@@ -18,13 +18,42 @@ class SpriteGeneratorTest < Test::Unit::TestCase
|
|
18
18
|
Dir.glob('test/output/*').each{|f| File.delete f }
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def test_should_user_horizontal_distribution
|
22
|
+
template = %q{ {{left}} }
|
23
|
+
generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :distribution => :horizontal })
|
24
|
+
css = generator.create
|
25
|
+
assert css.include?('240')
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_user_vertical_distribution
|
29
|
+
template = %q{ {{top}} }
|
30
|
+
generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :distribution => :vertical })
|
31
|
+
css = generator.create
|
32
|
+
`open #{@output}`
|
33
|
+
sleep 1
|
34
|
+
assert css.include?('240')
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_set_correct_context_filebasename_for_images_without_variations
|
22
38
|
template = %q{ {{file_basename}} }
|
23
39
|
generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :delimiter => '_' })
|
24
40
|
css = generator.create
|
25
41
|
assert css.include?('emoticon-evilgrin')
|
26
42
|
end
|
27
43
|
|
44
|
+
def test_should_set_correct_context_width_for_images_without_variations
|
45
|
+
template = %q{ {{width}} }
|
46
|
+
generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :delimiter => '_' })
|
47
|
+
css = generator.create
|
48
|
+
assert css.include?('16')
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_should_set_correct_context_top_for_images_without_variations
|
52
|
+
template = %q{ {{top}} }
|
53
|
+
generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :delimiter => '_' })
|
54
|
+
css = generator.create
|
55
|
+
assert !css.include?('-16')
|
56
|
+
end
|
28
57
|
|
29
58
|
def test_should_create_correct_context
|
30
59
|
template = %q{
|