spittle 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|