css-spriter 0.9.2
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/.gitignore +12 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +81 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/css-spriter +45 -0
- data/bin/png_info +61 -0
- data/examples/filter_util.rb +17 -0
- data/examples/sprites/.mtimes +12 -0
- data/examples/sprites/README +5 -0
- data/examples/sprites/fragment.css +0 -0
- data/examples/sprites/index.html +26 -0
- data/examples/sprites/many_sized_cats/.mtimes +3 -0
- data/examples/sprites/many_sized_cats/cat-on-keyboard.png +0 -0
- data/examples/sprites/many_sized_cats/darth_cat.png +0 -0
- data/examples/sprites/many_sized_cats/fragment.css +21 -0
- data/examples/sprites/many_sized_cats/music-keyboard-cat.png +0 -0
- data/examples/sprites/many_sized_cats/sprite.css +21 -0
- data/examples/sprites/many_sized_cats/sprite.png +0 -0
- data/examples/sprites/server.rb +10 -0
- data/examples/sprites/sprite.css +49 -0
- data/examples/sprites/words/.mtimes +4 -0
- data/examples/sprites/words/fragment.css +28 -0
- data/examples/sprites/words/latitude.png +0 -0
- data/examples/sprites/words/of.png +0 -0
- data/examples/sprites/words/set.png +0 -0
- data/examples/sprites/words/specified.png +0 -0
- data/examples/sprites/words/sprite.css +28 -0
- data/examples/sprites/words/sprite.png +0 -0
- data/init.rb +1 -0
- data/lib/css-spriter.rb +16 -0
- data/lib/css-spriter/directory_processor.rb +94 -0
- data/lib/css-spriter/image_data.rb +128 -0
- data/lib/css-spriter/mtime_tracker.rb +79 -0
- data/lib/css-spriter/png/chunk.rb +12 -0
- data/lib/css-spriter/png/file_header.rb +7 -0
- data/lib/css-spriter/png/filters.rb +80 -0
- data/lib/css-spriter/png/idat.rb +26 -0
- data/lib/css-spriter/png/iend.rb +9 -0
- data/lib/css-spriter/png/ihdr.rb +34 -0
- data/lib/css-spriter/png/image.rb +153 -0
- data/lib/css-spriter/png/parser.rb +54 -0
- data/lib/css-spriter/processor.rb +28 -0
- data/lib/css-spriter/sprite.rb +39 -0
- data/lib/css-spriter/stylesheet_builder.rb +22 -0
- data/spec/builders/image_builder.rb +23 -0
- data/spec/css_fragments/deep/style/fragment.css +1 -0
- data/spec/css_fragments/some/fragment.css +1 -0
- data/spec/expected_output/merge_right_test.png +0 -0
- data/spec/expected_output/write_test.png +0 -0
- data/spec/image_data_spec.rb +63 -0
- data/spec/images/lightening.png +0 -0
- data/spec/integration_spec.rb +151 -0
- data/spec/lib/file_header_spec.rb +10 -0
- data/spec/lib/idat_spec.rb +31 -0
- data/spec/lib/ihdr_spec.rb +43 -0
- data/spec/lib/image_spec.rb +42 -0
- data/spec/lib/parser_spec.rb +12 -0
- data/spec/lib/sprite_spec.rb +39 -0
- data/spec/mtime_tracking_spec.rb +69 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/sprite_dirs/words/latitude.png +0 -0
- data/spec/sprite_dirs/words/of.png +0 -0
- data/spec/sprite_dirs/words/set.png +0 -0
- data/spec/sprite_dirs/words/specified.png +0 -0
- data/spec/tmp/merge_right_test.png +0 -0
- data/spec/tmp/write_test.png +0 -0
- data/tasks/spriter_tasks.rake +25 -0
- metadata +148 -0
Binary file
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
|
+
|
4
|
+
describe 'PNG' do
|
5
|
+
before do
|
6
|
+
@img_dir = File.dirname(__FILE__) + '/images'
|
7
|
+
@expected_dir = File.dirname(__FILE__) + '/expected_output'
|
8
|
+
@tmp_dir = File.dirname(__FILE__) + '/tmp'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'can read and write a PNG' do
|
12
|
+
img = PNG::Image.open("#{@img_dir}/lightening.png")
|
13
|
+
img.write("#{@tmp_dir}/write_test.png", :filter_type => 0)
|
14
|
+
read("#{@expected_dir}/write_test.png").should == read("#{@tmp_dir}/write_test.png")
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can merge one PNG on the left of another' do
|
18
|
+
one = PNG::Image.image_data("#{@img_dir}/lightening.png", :rgba => false)
|
19
|
+
two = PNG::Image.image_data("#{@img_dir}/lightening.png", :rgba => false)
|
20
|
+
merged = one.merge_left two
|
21
|
+
PNG::Image.write("#{@tmp_dir}/merge_right_test.png", merged, :filter_type => 0)
|
22
|
+
read("#{@expected_dir}/merge_right_test.png").should == read("#{@tmp_dir}/merge_right_test.png")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "Dir sprite" do
|
27
|
+
before :all do
|
28
|
+
@dir = File.dirname(__FILE__) + "/sprite_dirs/words"
|
29
|
+
@spriter = DirectoryProcessor.new(@dir)
|
30
|
+
@sprite_file = @dir + "/sprite.png"
|
31
|
+
@css_file = @dir + "/fragment.css"
|
32
|
+
@spriter.write
|
33
|
+
end
|
34
|
+
|
35
|
+
after :all do
|
36
|
+
@spriter.cleanup
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "Sprite generation" do
|
40
|
+
it "provides the correct dir name" do
|
41
|
+
@spriter.dir_name.should == 'words'
|
42
|
+
end
|
43
|
+
|
44
|
+
it "find all the pngs in a directory" do
|
45
|
+
expected = ['latitude.png', 'of.png', 'set.png', 'specified.png']
|
46
|
+
images = @spriter.images
|
47
|
+
images.map{|f| f.split('/').last}.should == expected
|
48
|
+
end
|
49
|
+
|
50
|
+
it "sprites all the images in a directory" do
|
51
|
+
File.exists?(@sprite_file).should be_true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "CSS fragments" do
|
56
|
+
before :all do
|
57
|
+
@template = @dir + "/template.css"
|
58
|
+
@css = @spriter.css
|
59
|
+
end
|
60
|
+
|
61
|
+
after do
|
62
|
+
File.delete(@template) rescue nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should compose class names" do
|
66
|
+
@css.should include( ".words_latitude")
|
67
|
+
@css.should include( ".words_of" )
|
68
|
+
end
|
69
|
+
|
70
|
+
it "has the correct image path" do
|
71
|
+
@css.should include( "/sprite_dirs/words/sprite.png" )
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should write css fragments for a sprite" do
|
75
|
+
File.exists?(@css_file).should be_true
|
76
|
+
end
|
77
|
+
|
78
|
+
it "can be overidden by including a template.css in the sprite directory" do
|
79
|
+
File.open(@template, 'w'){|f| f.write("override")}
|
80
|
+
@spriter.write
|
81
|
+
@spriter.css.should include("override")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'Stylesheet generator' do
|
87
|
+
before :all do
|
88
|
+
@dir = File.dirname(__FILE__) + "/css_fragments"
|
89
|
+
@out = @dir + "/complete.css"
|
90
|
+
@builder = StylesheetBuilder.new(@dir)
|
91
|
+
@builder.output_file(@out)
|
92
|
+
@css = @builder.css
|
93
|
+
end
|
94
|
+
|
95
|
+
after :all do
|
96
|
+
@builder.cleanup
|
97
|
+
end
|
98
|
+
|
99
|
+
it "takes the css fragments and concatonates them into a single stylesheet" do
|
100
|
+
@css.should include( ".some_style" )
|
101
|
+
end
|
102
|
+
|
103
|
+
it "can handle nested folder structures" do
|
104
|
+
@css.should include( ".deep" )
|
105
|
+
end
|
106
|
+
|
107
|
+
it "writes the css file to the specified location" do
|
108
|
+
@builder.write
|
109
|
+
File.exists?(@out).should be_true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "Complete spriting process" do
|
114
|
+
before :all do
|
115
|
+
@dir = File.dirname(__FILE__) + "/sprite_dirs"
|
116
|
+
@css_file = @dir + "/sprite.css"
|
117
|
+
@spriter = CssSpriter::Processor.new(:path_prefix => "/images", :source => @dir, :css_file => @css_file)
|
118
|
+
@spriter.write
|
119
|
+
end
|
120
|
+
|
121
|
+
after :all do
|
122
|
+
@spriter.cleanup
|
123
|
+
#making sure it cleans things up - shitty place for these
|
124
|
+
File.exists?(@css_file).should be_false
|
125
|
+
File.exists?(@dir + "/words/sprite.png").should be_false
|
126
|
+
end
|
127
|
+
|
128
|
+
it "prepends a path prefix to all sprites in the css file" do
|
129
|
+
file = read(@css_file)
|
130
|
+
file.should include("/images/sprite_dirs/words")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "can find all the sprite directories" do
|
134
|
+
dirs = @spriter.directories.map{|d| d.split('/').last}
|
135
|
+
dirs.should include( "words" )
|
136
|
+
end
|
137
|
+
|
138
|
+
it "generates the css file at the appropriate location" do
|
139
|
+
File.exists?(@css_file).should be_true
|
140
|
+
end
|
141
|
+
|
142
|
+
it "creates sprites/css for all subfolders" do
|
143
|
+
File.exists?(@dir + "/words/sprite.png").should be_true
|
144
|
+
File.exists?(@dir + "/words/fragment.css").should be_true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def read(file_name)
|
149
|
+
File.read(file_name)
|
150
|
+
end
|
151
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::FileHeader do
|
4
|
+
# the pnd header provides sanity checks against most common file transfer errrors
|
5
|
+
it "outputs the PNG header" do
|
6
|
+
header = PNG::FileHeader.new.encode
|
7
|
+
header.should == [137, 80, 78, 71, 13, 10, 26, 10].pack("C*")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::IDAT do
|
4
|
+
before :each do
|
5
|
+
# it's just "Hello, World!" encoded
|
6
|
+
@data = "x\234\363H\315\311\311\327Q\b\317/\312IQ\004\000\037\236\004j"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "accepts compressed data" do
|
10
|
+
@idat = PNG::IDAT.new
|
11
|
+
|
12
|
+
@idat << @data
|
13
|
+
|
14
|
+
@idat.encode.should == @data
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can chunk its self" do
|
18
|
+
@idat = PNG::IDAT.new
|
19
|
+
@idat << @data
|
20
|
+
@chunk = chunk( "IDAT", @idat.encode )
|
21
|
+
|
22
|
+
@idat.to_chunk.should == @chunk
|
23
|
+
end
|
24
|
+
|
25
|
+
it "accepts uncompressed data for it's constructor" do
|
26
|
+
@idat = PNG::IDAT.new( "Hello, World!".unpack("C*") )
|
27
|
+
|
28
|
+
@idat.encode.should == @data
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::IHDR do
|
4
|
+
before :each do
|
5
|
+
@width = 40
|
6
|
+
@height = 40
|
7
|
+
@bit_depth = 8
|
8
|
+
@color_type = 2
|
9
|
+
|
10
|
+
@raw = [@width, @height, @bit_depth, @color_type, 0, 0, 0].pack("N2C5")
|
11
|
+
@chunk = chunk( "IHDR", @raw )
|
12
|
+
end
|
13
|
+
|
14
|
+
it "pulls out the width from the ihdr block" do
|
15
|
+
@header = PNG::IHDR.new_from_raw( @raw )
|
16
|
+
@header.width.should == @width
|
17
|
+
end
|
18
|
+
|
19
|
+
it "pulls out the height from the ihdr block" do
|
20
|
+
@header = PNG::IHDR.new_from_raw( @raw )
|
21
|
+
@header.height.should == @height
|
22
|
+
end
|
23
|
+
|
24
|
+
it "pulls out the bit depth from the ihdr block" do
|
25
|
+
@header = PNG::IHDR.new_from_raw( @raw )
|
26
|
+
@header.depth.should == @bit_depth
|
27
|
+
end
|
28
|
+
|
29
|
+
it "pulls out the color type from the ihdr block" do
|
30
|
+
@header = PNG::IHDR.new_from_raw( @raw )
|
31
|
+
@header.color_type.should == @color_type
|
32
|
+
end
|
33
|
+
|
34
|
+
it "encodes it's self properly" do
|
35
|
+
@header = PNG::IHDR.new_from_raw( @raw )
|
36
|
+
@header.encode.should == @raw
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be able to make a header chunk" do
|
40
|
+
@header = PNG::IHDR.new_from_raw( @raw )
|
41
|
+
@header.to_chunk.should == @chunk
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::Image do
|
4
|
+
before :each do
|
5
|
+
@sprite = Sprite.new
|
6
|
+
|
7
|
+
@builder = ImageBuilder.new
|
8
|
+
|
9
|
+
# 0 is the filter type for the row, then an RGB triplet since builder defaults to color type 2
|
10
|
+
@image1 = @builder.build( :width => 1, :height => 1, :name => "image1", :data => [0,1,2,3] )
|
11
|
+
@image2 = @builder.build( :width => 1, :height => 1, :name => "image2", :data => [0,4,5,6] )
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can merge left" do
|
15
|
+
result = @image1.to_image.merge_left @image2.to_image
|
16
|
+
|
17
|
+
result.should == [[4,5,6,1,2,3]]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can encode the rows with filter 0" do
|
21
|
+
@image1.filter_encoded_rows(0).should == [[0, 1, 2, 3]]
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
it "can encode the rows with filter 1" do
|
26
|
+
image = @builder.build( :width => 2, :height => 1, :name => "image1", :data => [0,1,2,3,4,5,6] )
|
27
|
+
|
28
|
+
# filter byte of 1
|
29
|
+
# first byte of pixel 2 - pixel 1 is 3
|
30
|
+
# second byte of pixel 2 - pixel 1 is 3.. etc
|
31
|
+
image.filter_encoded_rows(1).should == [[1, 1, 2, 3, 3, 3, 3]]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can encode the rows with filter 2" do
|
35
|
+
image = @builder.build( :width => 2, :height => 2, :name => "image1",
|
36
|
+
:data => [0,1,2,3,4,5,6,
|
37
|
+
0,0,0,0,0,0,0])
|
38
|
+
result = image
|
39
|
+
# filter byte of 2
|
40
|
+
result.filter_encoded_rows(2).should == [[2, 1, 2, 3, 4, 5, 6], [2, 255, 254, 253, 252, 251, 250]]
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::Parser do
|
4
|
+
it "errors out when the file header is wrong" do
|
5
|
+
bad_header = [5, 80, 78, 71, 13, 10, 26, 10].pack("C*")
|
6
|
+
file = StringIO.new( bad_header)
|
7
|
+
|
8
|
+
lambda {
|
9
|
+
Parser.go!( file )
|
10
|
+
}.should raise_error
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Sprite do
|
4
|
+
before :each do
|
5
|
+
@sprite = Sprite.new
|
6
|
+
@builder = ImageBuilder.new
|
7
|
+
|
8
|
+
#TODO - We should just create ImageData objects here
|
9
|
+
@image1 = @builder.build( :width => 50, :height => 50, :name => "image1").to_image
|
10
|
+
@image2 = @builder.build( :width => 50, :height => 50, :name => "image2").to_image
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can merge an image to the right" do
|
14
|
+
@sprite.append( @image1 )
|
15
|
+
@sprite.append( @image2 )
|
16
|
+
|
17
|
+
@sprite.images.should == [@image1, @image2]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "knows the location of each image in the sprite" do
|
21
|
+
@sprite.append( @image1 )
|
22
|
+
@sprite.append( @image2 )
|
23
|
+
|
24
|
+
@sprite.locations[@image1.name.to_sym].should == {:x => -( 0 ), :width=> @image1.width, :height => @image1.height }
|
25
|
+
@sprite.locations[@image2.name.to_sym].should == {:x => -( @image2.width ), :width=> @image2.width, :height => @image2.height }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "knows the height of the tallest image" do
|
29
|
+
max_height = 70
|
30
|
+
|
31
|
+
@image3 = @builder.build( :width => 50, :height => max_height, :name => "image3")
|
32
|
+
|
33
|
+
@sprite.append( @image1 )
|
34
|
+
@sprite.append( @image3 )
|
35
|
+
|
36
|
+
@sprite.max_height.should == max_height
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'fileutils'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
4
|
+
|
5
|
+
describe MtimeTracker do
|
6
|
+
describe "on a new directory" do
|
7
|
+
before do
|
8
|
+
@img_dir = File.dirname(__FILE__) + '/images'
|
9
|
+
@tracker = MtimeTracker.new(@img_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
File.delete(@img_dir + "/.mtimes") rescue nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "tells me there are changes" do
|
17
|
+
@tracker.has_changes?.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "tells me things have not changed after I update the tracking" do
|
21
|
+
@tracker.update
|
22
|
+
@tracker.has_changes?.should be_false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "on an existing directory" do
|
27
|
+
before do
|
28
|
+
@img_dir = File.dirname(__FILE__) + '/images'
|
29
|
+
MtimeTracker.new(@img_dir).update
|
30
|
+
@tracker = MtimeTracker.new(@img_dir)
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
File.delete(@img_dir + "/.mtimes") rescue nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it "tells me nothing has changed" do
|
38
|
+
@tracker.has_changes?.should be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "when a file has changed" do
|
42
|
+
before do
|
43
|
+
FileUtils.touch(@img_dir + "/lightening.png")
|
44
|
+
@tracker.reset
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns true from has_changes" do
|
48
|
+
@tracker.has_changes?.should be_true
|
49
|
+
@tracker.changeset.first.should include("/spec/images/lightening.png")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "file exclustions" do
|
54
|
+
before do
|
55
|
+
@img_dir = File.dirname(__FILE__) + '/images'
|
56
|
+
@tracker = MtimeTracker.new(@img_dir, :exclude => [/lightening/, "tacos"])
|
57
|
+
end
|
58
|
+
|
59
|
+
after do
|
60
|
+
File.delete(@img_dir + "/.mtimes") rescue nil
|
61
|
+
end
|
62
|
+
|
63
|
+
it "does not report excluded files as changed" do
|
64
|
+
FileUtils.touch(@img_dir + "/lightening.png")
|
65
|
+
@tracker.has_changes?.should be_false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# $LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
# $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'spec'
|
4
|
+
require 'spec/autorun'
|
5
|
+
|
6
|
+
require 'css-spriter'
|
7
|
+
|
8
|
+
require 'builders/image_builder'
|
9
|
+
|
10
|
+
Spec::Runner.configure do |config|
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def chunk(type, data)
|
15
|
+
to_check = type + data
|
16
|
+
[data.length].pack("N") + to_check + [Zlib.crc32(to_check)].pack("N")
|
17
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|