spittle 0.9.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/.gitignore +12 -0
- data/README +19 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/spittle +15 -0
- data/examples/sprites/README +5 -0
- data/examples/sprites/apple/apple.png +0 -0
- data/examples/sprites/apple/divider.png +0 -0
- data/examples/sprites/apple/downloads.png +0 -0
- data/examples/sprites/apple/fragment.css +63 -0
- data/examples/sprites/apple/iphone.png +0 -0
- data/examples/sprites/apple/itunes.png +0 -0
- data/examples/sprites/apple/mac.png +0 -0
- data/examples/sprites/apple/search.png +0 -0
- data/examples/sprites/apple/sprite.png +0 -0
- data/examples/sprites/apple/store.png +0 -0
- data/examples/sprites/apple/support.png +0 -0
- data/examples/sprites/fragment.css +0 -0
- data/examples/sprites/index.html +33 -0
- data/examples/sprites/server.rb +10 -0
- data/examples/sprites/sprite.css +91 -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 +24 -0
- data/examples/sprites/words/sprite.png +0 -0
- data/lib/spittle/chunk.rb +12 -0
- data/lib/spittle/directory_spriter.rb +63 -0
- data/lib/spittle/file_header.rb +7 -0
- data/lib/spittle/filters.rb +64 -0
- data/lib/spittle/idat.rb +26 -0
- data/lib/spittle/iend.rb +9 -0
- data/lib/spittle/ihdr.rb +30 -0
- data/lib/spittle/image.rb +114 -0
- data/lib/spittle/parser.rb +54 -0
- data/lib/spittle/processor.rb +28 -0
- data/lib/spittle/sprite.rb +39 -0
- data/lib/spittle/stylesheet_builder.rb +22 -0
- data/lib/spittle.rb +16 -0
- data/spec/builders/image_builder.rb +22 -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/images/lightening.png +0 -0
- data/spec/integration_spec.rb +134 -0
- data/spec/lib/file_header_spec.rb +10 -0
- data/spec/lib/idat_spec.rb +30 -0
- data/spec/lib/ihdr_spec.rb +43 -0
- data/spec/lib/image_spec.rb +19 -0
- data/spec/lib/parser_spec.rb +12 -0
- data/spec/lib/sprite_spec.rb +36 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +16 -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
- metadata +137 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
module PNG
|
2
|
+
class Image
|
3
|
+
|
4
|
+
def self.open( file_name )
|
5
|
+
name = File.basename( file_name, ".png" )
|
6
|
+
|
7
|
+
File.open(file_name, "r") do |f|
|
8
|
+
ihdr, idat = Parser.go!( f )
|
9
|
+
Image.new( ihdr, idat, name )
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize( ihdr, idat, name )
|
15
|
+
@ihdr = ihdr
|
16
|
+
@idat = idat
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :name
|
21
|
+
def width; @ihdr.width end
|
22
|
+
def height; @ihdr.height end
|
23
|
+
def depth; @ihdr.depth end
|
24
|
+
def color_type; @ihdr.color_type end
|
25
|
+
|
26
|
+
def compatible?(image)
|
27
|
+
self.height == image.height
|
28
|
+
end
|
29
|
+
|
30
|
+
def write(file_name)
|
31
|
+
File.open(file_name, 'w') do |f|
|
32
|
+
f.write(generate_png)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def merge_left( other )
|
37
|
+
l = other.rows
|
38
|
+
r = self.rows
|
39
|
+
|
40
|
+
data = l.zip r
|
41
|
+
|
42
|
+
#prepend the filter byte 0 = no filter
|
43
|
+
data.each { |row| row.unshift(0) }
|
44
|
+
data.flatten!
|
45
|
+
|
46
|
+
ihdr = IHDR.new( width + other.width, height, depth, color_type)
|
47
|
+
idat = IDAT.new( data )
|
48
|
+
img_name = "#{name}_#{other.name}"
|
49
|
+
|
50
|
+
Image.new( ihdr, idat, img_name )
|
51
|
+
end
|
52
|
+
|
53
|
+
#color types
|
54
|
+
RGB = 2
|
55
|
+
RGBA = 3
|
56
|
+
|
57
|
+
# check for RGB or RGBA
|
58
|
+
def pixel_width
|
59
|
+
( color_type == RGB ? 3 : 4)
|
60
|
+
end
|
61
|
+
|
62
|
+
def scanline_width
|
63
|
+
# + 1 adds filter byte
|
64
|
+
(width * pixel_width) + 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def rows
|
68
|
+
out = []
|
69
|
+
offset = 0
|
70
|
+
|
71
|
+
height.times do |scanline|
|
72
|
+
end_row = scanline_width + offset
|
73
|
+
row = @idat.uncompressed.slice(offset, scanline_width)
|
74
|
+
out << decode(scanline, row, out, pixel_width)
|
75
|
+
offset = end_row
|
76
|
+
end
|
77
|
+
out
|
78
|
+
end
|
79
|
+
|
80
|
+
def inspect
|
81
|
+
"color type: #{color_type}, depth: #{depth}, width: #{width}, height: #{height}"
|
82
|
+
end
|
83
|
+
private
|
84
|
+
|
85
|
+
def last_scanline(current, data)
|
86
|
+
(current - 1 < 0 ? [] : data[current - 1])
|
87
|
+
end
|
88
|
+
|
89
|
+
def decode(current, row, data, pixel_width)
|
90
|
+
filter_type = row.shift
|
91
|
+
|
92
|
+
process_row(row, last_scanline(current, data), filter_type, pixel_width)
|
93
|
+
end
|
94
|
+
|
95
|
+
def process_row(row, last_scanline, filter_type, pixel_width)
|
96
|
+
o = []
|
97
|
+
row.each_with_index do |e, i|
|
98
|
+
o[i] = Filters.call(filter_type, e, i, o, last_scanline, pixel_width)
|
99
|
+
end
|
100
|
+
o
|
101
|
+
end
|
102
|
+
|
103
|
+
def generate_png
|
104
|
+
file_header = PNG::FileHeader.new.encode
|
105
|
+
raw_data = @idat.uncompressed
|
106
|
+
|
107
|
+
ihdr = PNG::IHDR.new( width, height, depth, color_type ).to_chunk
|
108
|
+
idat = PNG::IDAT.new( raw_data ).to_chunk
|
109
|
+
iend = PNG::IEND.new.to_chunk
|
110
|
+
|
111
|
+
file_header + ihdr + idat + iend
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module PNG
|
2
|
+
class Parser
|
3
|
+
def self.go!(file)
|
4
|
+
#TODO: Wanted to remove instance go! and use initialize, didn't work.
|
5
|
+
#Weird
|
6
|
+
Parser.new.go!(file)
|
7
|
+
end
|
8
|
+
def go!( file )
|
9
|
+
check_header( file )
|
10
|
+
|
11
|
+
while(not file.eof?) do
|
12
|
+
parse_chunk(file)
|
13
|
+
end
|
14
|
+
|
15
|
+
[ @ihdr, @idat ]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def check_header( file )
|
20
|
+
header = file.read(8)
|
21
|
+
raise "Invalid PNG file header" unless ( header == FileHeader.new.encode)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_chunk(f)
|
25
|
+
len = f.read(4).unpack("N")
|
26
|
+
type = f.read(4)
|
27
|
+
data = f.read(len[0])
|
28
|
+
crc = f.read(4)
|
29
|
+
|
30
|
+
raise "invalid CRC for chunk type #{type}" if crc_invalid?( type, data, crc )
|
31
|
+
|
32
|
+
handle(type, data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle(type, data)
|
36
|
+
case(type)
|
37
|
+
when "IHDR"
|
38
|
+
@ihdr = PNG::IHDR.new_from_raw( data )
|
39
|
+
@width, @height, @depth, @color_type = @ihdr.to_a
|
40
|
+
when "IDAT"
|
41
|
+
@idat ||= PNG::IDAT.new
|
42
|
+
@idat << data
|
43
|
+
when "IEND"
|
44
|
+
# NOOP
|
45
|
+
else
|
46
|
+
#puts "Ignoring chunk type #{type}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def crc_invalid?( type, data, crc )
|
51
|
+
[Zlib.crc32( type + data )] != crc.unpack("N")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Spittle
|
2
|
+
class Processor
|
3
|
+
def initialize(opts)
|
4
|
+
@options = opts
|
5
|
+
@processors = dir_processors
|
6
|
+
@css_builder = StylesheetBuilder.new(@options[:source])
|
7
|
+
@css_builder.output_file(@options[:css_file] || @options[:source] + "/sprite.css")
|
8
|
+
end
|
9
|
+
|
10
|
+
def write
|
11
|
+
@processors.each{|d| d.write}
|
12
|
+
@css_builder.write
|
13
|
+
end
|
14
|
+
|
15
|
+
def directories
|
16
|
+
Dir.glob(@options[:source] + "/**/").map{|d| d.gsub(/\/$/, "")}
|
17
|
+
end
|
18
|
+
|
19
|
+
def dir_processors
|
20
|
+
directories.map{|d| DirectoryProcessor.new(d)}
|
21
|
+
end
|
22
|
+
|
23
|
+
def cleanup
|
24
|
+
@processors.each{|d| d.cleanup}
|
25
|
+
@css_builder.cleanup
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module PNG
|
2
|
+
class ImageFormatException < Exception; end
|
3
|
+
class Sprite
|
4
|
+
attr_reader :images
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@images = []
|
8
|
+
@locations = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def append( image )
|
12
|
+
@images.each do |i|
|
13
|
+
unless i.compatible? image
|
14
|
+
raise ImageFormatException.new("Incompatible image #{i}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@images << image
|
18
|
+
end
|
19
|
+
|
20
|
+
def locations
|
21
|
+
@images.inject(0) do |x, image|
|
22
|
+
@locations[image.name.to_sym] = { :x => -(x),
|
23
|
+
:width => image.width,
|
24
|
+
:height => image.height}
|
25
|
+
image.width + x
|
26
|
+
end
|
27
|
+
@locations
|
28
|
+
end
|
29
|
+
|
30
|
+
def write( output_filename )
|
31
|
+
return if @images.empty?
|
32
|
+
|
33
|
+
# head is the last image, then we merge left
|
34
|
+
head, *tail = @images.reverse
|
35
|
+
result = tail.inject( head ){ |head, image| head.merge_left( image ) }
|
36
|
+
result.write( output_filename )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class StylesheetBuilder
|
2
|
+
def initialize(dir)
|
3
|
+
@dir = dir
|
4
|
+
@output_file = @dir + "/sprite.css"
|
5
|
+
end
|
6
|
+
|
7
|
+
def output_file(file)
|
8
|
+
@output_file = file
|
9
|
+
end
|
10
|
+
|
11
|
+
def css
|
12
|
+
@css ||= Dir.glob(@dir + "/**/fragment.css").inject("") {|acc, f| acc + File.read(f)}
|
13
|
+
end
|
14
|
+
|
15
|
+
def write
|
16
|
+
File.open(@output_file, 'w') {|f| f.write(css)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def cleanup
|
20
|
+
File.delete(@output_file) rescue {}
|
21
|
+
end
|
22
|
+
end
|
data/lib/spittle.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
$:.unshift( File.dirname( __FILE__ ))
|
4
|
+
|
5
|
+
require 'spittle/file_header'
|
6
|
+
require 'spittle/parser'
|
7
|
+
require 'spittle/filters'
|
8
|
+
require 'spittle/chunk'
|
9
|
+
require 'spittle/ihdr'
|
10
|
+
require 'spittle/idat'
|
11
|
+
require 'spittle/iend'
|
12
|
+
require 'spittle/image'
|
13
|
+
require 'spittle/sprite'
|
14
|
+
require 'spittle/directory_spriter'
|
15
|
+
require 'spittle/stylesheet_builder'
|
16
|
+
require 'spittle/processor'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ImageBuilder
|
2
|
+
def initialize( general_options={} )
|
3
|
+
default_options = {
|
4
|
+
:width => 100,
|
5
|
+
:height => 100,
|
6
|
+
:data => [],
|
7
|
+
:name => "test"
|
8
|
+
}
|
9
|
+
|
10
|
+
@general_options = default_options.merge general_options
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def build( specific_options={} )
|
15
|
+
args = @general_options.merge specific_options
|
16
|
+
|
17
|
+
ihdr = PNG::IHDR.new( args[:width], args[:height] )
|
18
|
+
idat = PNG::IDAT.new( args[:data] )
|
19
|
+
|
20
|
+
PNG::Image.new( ihdr, idat, args[:name] )
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
.deep {}
|
@@ -0,0 +1 @@
|
|
1
|
+
.some_style {}
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,134 @@
|
|
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")
|
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.open("#{@img_dir}/lightening.png")
|
19
|
+
two = PNG::Image.open("#{@img_dir}/lightening.png")
|
20
|
+
merged = one.merge_left two
|
21
|
+
merged.write("#{@tmp_dir}/merge_right_test.png")
|
22
|
+
read("#{@expected_dir}/merge_right_test.png").should == read("#{@tmp_dir}/merge_right_test.png")
|
23
|
+
end
|
24
|
+
|
25
|
+
def read(file_name)
|
26
|
+
File.read(file_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "Dir sprite" do
|
31
|
+
before :all do
|
32
|
+
@dir = File.dirname(__FILE__) + "/sprite_dirs/words"
|
33
|
+
@spriter = DirectoryProcessor.new(@dir)
|
34
|
+
@sprite_file = @dir + "/sprite.png"
|
35
|
+
@css_file = @dir + "/fragment.css"
|
36
|
+
@spriter.write
|
37
|
+
end
|
38
|
+
|
39
|
+
after :all do
|
40
|
+
@spriter.cleanup
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "Sprite generation" do
|
44
|
+
it "provides the correct dir name" do
|
45
|
+
@spriter.dir_name.should == 'words'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "find all the pngs in a directory" do
|
49
|
+
expected = ['latitude.png', 'of.png', 'set.png', 'specified.png']
|
50
|
+
images = @spriter.images
|
51
|
+
images.map{|f| f.split('/').last}.should == expected
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sprites all the images in a directory" do
|
55
|
+
File.exists?(@sprite_file).should be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "CSS fragments" do
|
60
|
+
before :all do
|
61
|
+
@css = @spriter.css
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should compose class names" do
|
65
|
+
@css.should include( ".words_latitude")
|
66
|
+
@css.should include( ".words_of" )
|
67
|
+
end
|
68
|
+
|
69
|
+
it "has the correct image path" do
|
70
|
+
@css.should include( "/sprite_dirs/words/sprite.png" )
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should write css fragments for a sprite" do
|
74
|
+
File.exists?(@css_file).should be_true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'Stylesheet generator' do
|
80
|
+
before :all do
|
81
|
+
@dir = File.dirname(__FILE__) + "/css_fragments"
|
82
|
+
@out = @dir + "/complete.css"
|
83
|
+
@builder = StylesheetBuilder.new(@dir)
|
84
|
+
@builder.output_file(@out)
|
85
|
+
@css = @builder.css
|
86
|
+
end
|
87
|
+
|
88
|
+
after :all do
|
89
|
+
@builder.cleanup
|
90
|
+
end
|
91
|
+
|
92
|
+
it "takes the css fragments and concatonates them into a single stylesheet" do
|
93
|
+
@css.should include( ".some_style" )
|
94
|
+
end
|
95
|
+
|
96
|
+
it "can handle nested folder structures" do
|
97
|
+
@css.should include( ".deep" )
|
98
|
+
end
|
99
|
+
|
100
|
+
it "writes the css file to the specified location" do
|
101
|
+
@builder.write
|
102
|
+
File.exists?(@out).should be_true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "Complete spriting process" do
|
107
|
+
before :all do
|
108
|
+
@dir = File.dirname(__FILE__) + "/sprite_dirs"
|
109
|
+
@css_file = @dir + "/sprite.css"
|
110
|
+
@spittle = Spittle::Processor.new(:source => @dir, :css_file => @css_file)
|
111
|
+
@spittle.write
|
112
|
+
end
|
113
|
+
|
114
|
+
after :all do
|
115
|
+
@spittle.cleanup
|
116
|
+
#making sure it cleans things up - shitty place for these
|
117
|
+
File.exists?(@css_file).should be_false
|
118
|
+
File.exists?(@dir + "/words/sprite.png").should be_false
|
119
|
+
end
|
120
|
+
|
121
|
+
it "can find all the sprite directories" do
|
122
|
+
dirs = @spittle.directories.map{|d| d.split('/').last}
|
123
|
+
dirs.should include( "words" )
|
124
|
+
end
|
125
|
+
|
126
|
+
it "generates the css file at the appropriate location" do
|
127
|
+
File.exists?(@css_file).should be_true
|
128
|
+
end
|
129
|
+
|
130
|
+
it "creates sprites/css for all subfolders" do
|
131
|
+
File.exists?(@dir + "/words/sprite.png").should be_true
|
132
|
+
File.exists?(@dir + "/words/fragment.css").should be_true
|
133
|
+
end
|
134
|
+
end
|
@@ -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,30 @@
|
|
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
|
+
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,19 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::Image do
|
4
|
+
before :each do
|
5
|
+
@sprite = PNG::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.merge_left @image2
|
16
|
+
|
17
|
+
result.rows.should == [[4,5,6,1,2,3]]
|
18
|
+
end
|
19
|
+
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,36 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe PNG::Sprite do
|
4
|
+
before :each do
|
5
|
+
@sprite = PNG::Sprite.new
|
6
|
+
@builder = ImageBuilder.new
|
7
|
+
|
8
|
+
@image1 = @builder.build( :width => 50, :height => 50, :name => "image1")
|
9
|
+
@image2 = @builder.build( :width => 50, :height => 50, :name => "image2")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can merge an image to the right" do
|
13
|
+
@sprite.append( @image1 )
|
14
|
+
@sprite.append( @image2 )
|
15
|
+
|
16
|
+
@sprite.images.should == [@image1, @image2]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "knows the location of each image in the sprite" do
|
20
|
+
@sprite.append( @image1 )
|
21
|
+
@sprite.append( @image2 )
|
22
|
+
|
23
|
+
@sprite.locations[@image1.name.to_sym].should == {:x => -( 0 ), :width=> @image1.width, :height => @image1.height }
|
24
|
+
@sprite.locations[@image2.name.to_sym].should == {:x => -( @image2.width ), :width=> @image2.width, :height => @image2.height }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises a pretty exception when the images are incompatible" do
|
28
|
+
taller_image = @builder.build( :width => 50, :height => 60, :name => "image")
|
29
|
+
|
30
|
+
lambda do
|
31
|
+
@sprite.append taller_image
|
32
|
+
@sprite.append @image1
|
33
|
+
end.should raise_error(PNG::ImageFormatException)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'spittle'
|
4
|
+
require 'spec'
|
5
|
+
require 'spec/autorun'
|
6
|
+
|
7
|
+
require 'builders/image_builder'
|
8
|
+
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def chunk(type, data)
|
14
|
+
to_check = type + data
|
15
|
+
[data.length].pack("N") + to_check + [Zlib.crc32(to_check)].pack("N")
|
16
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|