lash-sprites 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +165 -0
  4. data/README +38 -0
  5. data/Rakefile +38 -0
  6. data/examples/css/css.rb +17 -0
  7. data/examples/css/css_cli.rb +29 -0
  8. data/examples/example_imgs/1.png +0 -0
  9. data/examples/example_imgs/10.png +0 -0
  10. data/examples/example_imgs/11.png +0 -0
  11. data/examples/example_imgs/12.png +0 -0
  12. data/examples/example_imgs/13.png +0 -0
  13. data/examples/example_imgs/14.png +0 -0
  14. data/examples/example_imgs/15.png +0 -0
  15. data/examples/example_imgs/16.png +0 -0
  16. data/examples/example_imgs/17.png +0 -0
  17. data/examples/example_imgs/18.png +0 -0
  18. data/examples/example_imgs/19.png +0 -0
  19. data/examples/example_imgs/2.png +0 -0
  20. data/examples/example_imgs/20.png +0 -0
  21. data/examples/example_imgs/21.png +0 -0
  22. data/examples/example_imgs/22.png +0 -0
  23. data/examples/example_imgs/23.png +0 -0
  24. data/examples/example_imgs/24.png +0 -0
  25. data/examples/example_imgs/25.png +0 -0
  26. data/examples/example_imgs/26.png +0 -0
  27. data/examples/example_imgs/27.png +0 -0
  28. data/examples/example_imgs/28.png +0 -0
  29. data/examples/example_imgs/29.png +0 -0
  30. data/examples/example_imgs/3.png +0 -0
  31. data/examples/example_imgs/30.png +0 -0
  32. data/examples/example_imgs/31.png +0 -0
  33. data/examples/example_imgs/32.png +0 -0
  34. data/examples/example_imgs/33.png +0 -0
  35. data/examples/example_imgs/34.png +0 -0
  36. data/examples/example_imgs/35.png +0 -0
  37. data/examples/example_imgs/36.png +0 -0
  38. data/examples/example_imgs/37.png +0 -0
  39. data/examples/example_imgs/38.png +0 -0
  40. data/examples/example_imgs/39.png +0 -0
  41. data/examples/example_imgs/4.png +0 -0
  42. data/examples/example_imgs/40.png +0 -0
  43. data/examples/example_imgs/41.png +0 -0
  44. data/examples/example_imgs/42.png +0 -0
  45. data/examples/example_imgs/43.png +0 -0
  46. data/examples/example_imgs/44.png +0 -0
  47. data/examples/example_imgs/45.png +0 -0
  48. data/examples/example_imgs/46.png +0 -0
  49. data/examples/example_imgs/47.png +0 -0
  50. data/examples/example_imgs/48.png +0 -0
  51. data/examples/example_imgs/49.png +0 -0
  52. data/examples/example_imgs/5.png +0 -0
  53. data/examples/example_imgs/50.png +0 -0
  54. data/examples/example_imgs/51.png +0 -0
  55. data/examples/example_imgs/52.png +0 -0
  56. data/examples/example_imgs/53.png +0 -0
  57. data/examples/example_imgs/54.png +0 -0
  58. data/examples/example_imgs/55.png +0 -0
  59. data/examples/example_imgs/56.png +0 -0
  60. data/examples/example_imgs/57.png +0 -0
  61. data/examples/example_imgs/58.png +0 -0
  62. data/examples/example_imgs/59.png +0 -0
  63. data/examples/example_imgs/6.png +0 -0
  64. data/examples/example_imgs/60.png +0 -0
  65. data/examples/example_imgs/7.png +0 -0
  66. data/examples/example_imgs/8.png +0 -0
  67. data/examples/example_imgs/9.png +0 -0
  68. data/examples/packing/packing.rb +19 -0
  69. data/lash-sprites.gemspec +21 -0
  70. data/lib/.DS_Store +0 -0
  71. data/lib/lash-sprites/.DS_Store +0 -0
  72. data/lib/lash-sprites/block.rb +34 -0
  73. data/lib/lash-sprites/css.rb +27 -0
  74. data/lib/lash-sprites/graphics_manager/gd2.rb +43 -0
  75. data/lib/lash-sprites/graphics_manager/graphics_manager.rb +22 -0
  76. data/lib/lash-sprites/graphics_manager/rmagick.rb +45 -0
  77. data/lib/lash-sprites/image.rb +46 -0
  78. data/lib/lash-sprites/packer/both_smart.rb +90 -0
  79. data/lib/lash-sprites/packer/both_split.rb +171 -0
  80. data/lib/lash-sprites/packer/even.rb +78 -0
  81. data/lib/lash-sprites/packer/horizontal_smart.rb +72 -0
  82. data/lib/lash-sprites/packer/horizontal_split.rb +156 -0
  83. data/lib/lash-sprites/packer/horizontal_stack.rb +22 -0
  84. data/lib/lash-sprites/packer/ratio.rb +94 -0
  85. data/lib/lash-sprites/packer/vertical_smart.rb +72 -0
  86. data/lib/lash-sprites/packer/vertical_split.rb +164 -0
  87. data/lib/lash-sprites/packer/vertical_stack.rb +23 -0
  88. data/lib/lash-sprites/sprite.rb +272 -0
  89. data/lib/lash-sprites/version.rb +5 -0
  90. data/test/b_graphics.rb +37 -0
  91. data/test/b_packers.rb +53 -0
  92. data/test/imgs/1.png +0 -0
  93. data/test/imgs/10.png +0 -0
  94. data/test/imgs/11.png +0 -0
  95. data/test/imgs/12.png +0 -0
  96. data/test/imgs/13.png +0 -0
  97. data/test/imgs/14.png +0 -0
  98. data/test/imgs/15.png +0 -0
  99. data/test/imgs/16.png +0 -0
  100. data/test/imgs/17.png +0 -0
  101. data/test/imgs/18.png +0 -0
  102. data/test/imgs/19.png +0 -0
  103. data/test/imgs/2.png +0 -0
  104. data/test/imgs/20.png +0 -0
  105. data/test/imgs/21.png +0 -0
  106. data/test/imgs/22.png +0 -0
  107. data/test/imgs/23.png +0 -0
  108. data/test/imgs/24.png +0 -0
  109. data/test/imgs/25.png +0 -0
  110. data/test/imgs/26.png +0 -0
  111. data/test/imgs/27.png +0 -0
  112. data/test/imgs/28.png +0 -0
  113. data/test/imgs/29.png +0 -0
  114. data/test/imgs/3.png +0 -0
  115. data/test/imgs/30.png +0 -0
  116. data/test/imgs/31.png +0 -0
  117. data/test/imgs/32.png +0 -0
  118. data/test/imgs/33.png +0 -0
  119. data/test/imgs/34.png +0 -0
  120. data/test/imgs/35.png +0 -0
  121. data/test/imgs/36.png +0 -0
  122. data/test/imgs/37.png +0 -0
  123. data/test/imgs/38.png +0 -0
  124. data/test/imgs/39.png +0 -0
  125. data/test/imgs/4.png +0 -0
  126. data/test/imgs/40.png +0 -0
  127. data/test/imgs/41.png +0 -0
  128. data/test/imgs/42.png +0 -0
  129. data/test/imgs/43.png +0 -0
  130. data/test/imgs/44.png +0 -0
  131. data/test/imgs/45.png +0 -0
  132. data/test/imgs/46.png +0 -0
  133. data/test/imgs/47.png +0 -0
  134. data/test/imgs/48.png +0 -0
  135. data/test/imgs/49.png +0 -0
  136. data/test/imgs/5.png +0 -0
  137. data/test/imgs/50.png +0 -0
  138. data/test/imgs/51.png +0 -0
  139. data/test/imgs/52.png +0 -0
  140. data/test/imgs/53.png +0 -0
  141. data/test/imgs/54.png +0 -0
  142. data/test/imgs/55.png +0 -0
  143. data/test/imgs/56.png +0 -0
  144. data/test/imgs/57.png +0 -0
  145. data/test/imgs/58.png +0 -0
  146. data/test/imgs/59.png +0 -0
  147. data/test/imgs/6.png +0 -0
  148. data/test/imgs/60.png +0 -0
  149. data/test/imgs/7.png +0 -0
  150. data/test/imgs/8.png +0 -0
  151. data/test/imgs/9.png +0 -0
  152. data/test/t_gd2.rb +28 -0
  153. data/test/t_generic.rb +183 -0
  154. data/test/t_rmagick.rb +29 -0
  155. metadata +269 -0
@@ -0,0 +1,45 @@
1
+ require 'ruby_sprites/graphics_manager/graphics_manager'
2
+
3
+ module RubySprites
4
+ module GraphicsManager
5
+ class Rmagick < GraphicsManager
6
+
7
+ DESCRIPTION = 'RMagick'
8
+
9
+ def self.availible?
10
+ begin
11
+ require 'rubygems'
12
+ require 'RMagick'
13
+ rescue LoadError
14
+ return false
15
+ end
16
+
17
+ return Magick::Version.match(/^RMagick 2/)
18
+ end
19
+
20
+ def combine(images, width, height)
21
+ image = Magick::Image.new(width, height) { self.background_color = '#0000' }
22
+ images.each do |path, img|
23
+ next unless img.exists?
24
+ i = Magick::Image.read(@sprite.file_root + path).first
25
+ drawer = Magick::Draw.new
26
+ w = width - img.x
27
+ w = img.width if img.width < w
28
+ h = height - img.y
29
+ h = img.height if img.height < h
30
+ drawer.composite(img.x, img.y, w, h, i)
31
+ drawer.draw(image)
32
+ i.destroy!
33
+ end
34
+ image.write(@sprite.image_file)
35
+ end
36
+
37
+ def get_info(path)
38
+ img = Magick::Image.read(@sprite.file_root + path).first
39
+ info = {:width => img.columns, :height => img.rows}
40
+ img.destroy!
41
+ return info
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ module RubySprites
2
+ class Image
3
+
4
+ attr_accessor :x, :y
5
+ attr_reader :path, :width, :height
6
+
7
+ def initialize(path, sprite, x = 0, y = 0, width = nil, height = nil)
8
+ @path = path
9
+ @sprite = sprite
10
+ @x = x
11
+ @y = y
12
+
13
+ @width = nil
14
+ @height = nil
15
+
16
+ if exists?
17
+
18
+ if !sprite.mtime.nil? && mtime < sprite.mtime
19
+ @width = width
20
+ @height = height
21
+ end
22
+
23
+ if @width.nil? || @height.nil?
24
+ info = @sprite.graphics_manager.get_info(path)
25
+ @width = info[:width]
26
+ @height = info[:height]
27
+ end
28
+ end
29
+ end
30
+
31
+ def exists?
32
+ return File.exists?(@sprite.file_root + @path)
33
+ end
34
+
35
+ def area
36
+ return nil if @width.nil?
37
+ @area = @width * @height if @area.nil?
38
+ return @area
39
+ end
40
+
41
+ def mtime
42
+ return File.mtime(@sprite.file_root + @path) if exists?
43
+ return nil
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,90 @@
1
+ require 'ruby_sprites/image'
2
+ require 'ruby_sprites/block'
3
+
4
+ module RubySprites
5
+ module Packer
6
+ module BothSmart
7
+
8
+ def self.pack(images, options = {})
9
+ width = 0
10
+ height = 0
11
+
12
+ images.sort! do |a, b|
13
+ b.area <=> a.area
14
+ end
15
+
16
+ blocks = []
17
+
18
+ images.each do |img|
19
+ next unless img.exists?
20
+ smallest = nil
21
+ exact = nil
22
+ blocks.each do |block|
23
+ next unless block.fits?(img)
24
+
25
+ exact = block if (img.width == block.width || img.height == block.height) && (exact.nil? || block.area < exact.area)
26
+ smallest = block if smallest.nil? || block.area < smallest.area
27
+
28
+ break if smallest.area == img.area
29
+ end
30
+
31
+ if smallest
32
+ if exact
33
+ img.x = exact.x
34
+ img.y = exact.y
35
+ blocks.concat(blocks.delete(exact).split(img))
36
+ else
37
+ img.x = smallest.x
38
+ img.y = smallest.y
39
+ blocks.concat(blocks.delete(smallest).split(img))
40
+ end
41
+ else
42
+ if width == 0 && height == 0
43
+ b = Block.new(0, 0, img.width, img.height)
44
+ width = img.width
45
+ height = img.height
46
+ blocks.concat(b.split(img))
47
+ else
48
+ if img.height > height
49
+ new_area_right = width * (img.height - height)
50
+ else
51
+ new_area_right = img.width * (height - img.height)
52
+ end
53
+
54
+ if img.width > width
55
+ new_area_below = height * (img.width - width)
56
+ else
57
+ new_area_below = img.height * (width - img.width)
58
+ end
59
+
60
+ if new_area_below > new_area_right
61
+ if img.height > height
62
+ blocks.push(Block.new(0, height, width, img.height - height))
63
+ height = img.height
64
+ elsif img.height < height
65
+ blocks.push(Block.new(width, img.height, img.width, height - img.height))
66
+ end
67
+ img.x = width
68
+ img.y = 0
69
+ width += img.width
70
+ else
71
+ if img.width > width
72
+ blocks.push(Block.new(width, 0, img.width - width, height))
73
+ width = img.width
74
+ elsif img.width < width
75
+ blocks.push(Block.new(img.width, height, width - img.width, img.height))
76
+ end
77
+ img.x = 0
78
+ img.y = height
79
+ height += img.height
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ return {:width => width, :height => height}
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,171 @@
1
+ require 'ruby_sprites/image'
2
+ require 'ruby_sprites/block'
3
+
4
+ module RubySprites
5
+ module Packer
6
+ module BothSplit
7
+
8
+ def self.pack(images, options = {})
9
+ width = 0
10
+ height = 0
11
+
12
+ images = images.dup
13
+
14
+ images.sort! do |a, b|
15
+ a.area <=> b.area
16
+ end
17
+
18
+ block_heap = Heap.new {|a,b| b.area <=> a.area}
19
+
20
+ while !images.empty?
21
+ if block_heap.empty?
22
+ img = images.pop
23
+ if width == 0 && height == 0
24
+ img.x = 0
25
+ img.y = 0
26
+ width = img.width
27
+ height = img.height
28
+ else
29
+ if img.height > height
30
+ new_area_right = width * (img.height - height)
31
+ else
32
+ new_area_right = img.width * (height - img.height)
33
+ end
34
+
35
+ if img.width > width
36
+ new_area_below = height * (img.width - width)
37
+ else
38
+ new_area_below = img.height * (width - img.width)
39
+ end
40
+
41
+ if new_area_below > new_area_right
42
+ if img.height > height
43
+ block_heap.insert(Block.new(0, height, width, img.height - height))
44
+ height = img.height
45
+ elsif img.height < height
46
+ block_heap.insert(Block.new(width, img.height, img.width, height - img.height))
47
+ end
48
+ img.x = width
49
+ img.y = 0
50
+ width += img.width
51
+ else
52
+ if img.width > width
53
+ block_heap.insert(Block.new(width, 0, img.width - width, height))
54
+ width = img.width
55
+ elsif img.width < width
56
+ block_heap.insert(Block.new(img.width, height, width - img.width, img.height))
57
+ end
58
+ img.x = 0
59
+ img.y = height
60
+ height += img.height
61
+ end
62
+ end
63
+ else
64
+ while !block_heap.empty? && !images.empty?
65
+ block = block_heap.remove
66
+ cur_img = nil
67
+ cur_exact = nil
68
+ images.each_index do |i|
69
+ break if images[i].area > block.area
70
+ cur_img = i if block.fits?(images[i])
71
+ cur_exact = i if block.fits?(images[i]) && (block.width == images[i].width || block.height == images[i].height)
72
+ end
73
+ if !cur_exact.nil?
74
+ img = images.delete_at(cur_exact)
75
+ img.x = block.x
76
+ img.y = block.y
77
+ split_block(block, img).each do |b|
78
+ block_heap.insert(b)
79
+ end
80
+ elsif !cur_img.nil?
81
+ img = images.delete_at(cur_img)
82
+ img.x = block.x
83
+ img.y = block.y
84
+ split_block(block, img).each do |b|
85
+ block_heap.insert(b)
86
+ end
87
+ else
88
+ # Nothing will fit in this block, we are throwing it out
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ return {:width => width, :height => height}
95
+ end
96
+
97
+ def self.split_block(block, img)
98
+ blocks = []
99
+ img.x = block.x
100
+ img.y = block.y
101
+ if (block.width - img.width) * img.height > (block.height - img.height) * img.width
102
+ blocks.push Block.new(block.x + img.width, block.y, block.width - img.width, block.height) if block.width != img.width
103
+ blocks.push Block.new(block.x, block.y + img.height, img.width, block.height - img.height) if block.height != img.height
104
+ else
105
+ blocks.push Block.new(block.x + img.width, block.y, block.width - img.width, img.height) if block.width != img.width
106
+ blocks.push Block.new(block.x, block.y + img.height, block.width, block.height - img.height) if block.height != img.height
107
+ end
108
+ return blocks
109
+ end
110
+
111
+ protected
112
+
113
+ class Heap
114
+ def initialize(&comparer)
115
+ @comparer = comparer
116
+ @heap = [0]
117
+ end
118
+
119
+ def insert(el)
120
+ pos = @heap.length
121
+ @heap.push(el)
122
+ new_pos = pos
123
+ while new_pos != 1 && @comparer.call(el, @heap[new_pos / 2]) < 0
124
+ new_pos /= 2
125
+ end
126
+
127
+ while(pos > new_pos)
128
+ @heap[pos] = @heap[pos / 2]
129
+ pos /= 2
130
+ end
131
+ @heap[pos] = el
132
+ end
133
+
134
+ def remove
135
+ return nil if @heap.length == 1
136
+ el = @heap[1]
137
+ shifter = @heap.pop
138
+ if @heap.length != 1
139
+ pos = 1
140
+ while(!@heap[pos * 2].nil?)
141
+ lesser_pos = pos * 2
142
+ lesser_pos += 1 if !@heap[pos * 2 + 1].nil? && @comparer.call(@heap[pos * 2 + 1], @heap[pos * 2]) < 0
143
+ if(@comparer.call(@heap[lesser_pos], shifter) < 0)
144
+ @heap[pos] = @heap[lesser_pos]
145
+ pos = lesser_pos
146
+ else
147
+ break
148
+ end
149
+ end
150
+
151
+ @heap[pos] = shifter
152
+ end
153
+ return el
154
+ end
155
+
156
+ def peek
157
+ return nil if @heap.length == 1
158
+ return @heap[1]
159
+ end
160
+
161
+ def empty?
162
+ return @heap.length == 1
163
+ end
164
+
165
+ def inspect
166
+ return @heap.to_s
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,78 @@
1
+ require 'ruby_sprites/image'
2
+ require 'ruby_sprites/block'
3
+
4
+ module RubySprites
5
+ module Packer
6
+ module Even
7
+
8
+ def self.pack(images, options = {})
9
+ width = 0
10
+ height = 0
11
+
12
+ images.sort! do |a, b|
13
+ b.area <=> a.area
14
+ end
15
+
16
+ blocks = []
17
+
18
+ images.each do |img|
19
+ next unless img.exists?
20
+ smallest = nil
21
+ exact = nil
22
+ blocks.each do |block|
23
+ next unless block.fits?(img)
24
+
25
+ exact = block if (img.width == block.width || img.height == block.height) && (exact.nil? || block.area < exact.area)
26
+ smallest = block if smallest.nil? || block.area < smallest.area
27
+
28
+ break if smallest.area == img.area
29
+ end
30
+
31
+ if smallest
32
+ if exact
33
+ img.x = exact.x
34
+ img.y = exact.y
35
+ blocks.concat(blocks.delete(exact).split(img))
36
+ else
37
+ img.x = smallest.x
38
+ img.y = smallest.y
39
+ blocks.concat(blocks.delete(smallest).split(img))
40
+ end
41
+ else
42
+ if width == 0 && height == 0
43
+ b = Block.new(0, 0, img.width, img.height)
44
+ width = img.width
45
+ height = img.height
46
+ blocks.concat(b.split(img))
47
+ else
48
+ if height > width
49
+ if img.height > height
50
+ blocks.push(Block.new(0, height, width, img.height - height))
51
+ height = img.height
52
+ elsif img.height < height
53
+ blocks.push(Block.new(width, img.height, img.width, height - img.height))
54
+ end
55
+ img.x = width
56
+ img.y = 0
57
+ width += img.width
58
+ else
59
+ if img.width > width
60
+ blocks.push(Block.new(width, 0, img.width - width, height))
61
+ width = img.width
62
+ elsif img.width < width
63
+ blocks.push(Block.new(img.width, height, width - img.width, img.height))
64
+ end
65
+ img.x = 0
66
+ img.y = height
67
+ height += img.height
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ return {:width => width, :height => height}
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,72 @@
1
+ require 'ruby_sprites/image'
2
+ require 'ruby_sprites/block'
3
+
4
+ module RubySprites
5
+ module Packer
6
+ module HorizontalSmart
7
+
8
+ def self.pack(images, options = {})
9
+ width = 0
10
+ height = 0
11
+
12
+ images.sort! do |a, b|
13
+ if a.height == b.height
14
+ b.width <=> a.width
15
+ else
16
+ b.height <=> a.height
17
+ end
18
+ end
19
+
20
+ blocks = []
21
+
22
+ images.each do |img|
23
+ next unless img.exists?
24
+ smallest = nil
25
+ exact = nil
26
+ blocks.each do |block|
27
+ next unless block.fits?(img)
28
+
29
+ exact = block if (img.width == block.width || img.height == block.height) && (exact.nil? || block.area < exact.area)
30
+ smallest = block if smallest.nil? || block.area < smallest.area
31
+
32
+ break if smallest.area == img.area
33
+ end
34
+
35
+ if smallest
36
+ if exact
37
+ blocks.concat(split_block(blocks.delete(exact), img))
38
+ else
39
+ blocks.concat(split_block(blocks.delete(smallest), img))
40
+ end
41
+ else
42
+ if width == 0 && height == 0
43
+ b = Block.new(0, 0, img.width, img.height)
44
+ width = img.width
45
+ height = img.height
46
+ else
47
+ b = Block.new(width, 0, img.width, height)
48
+ width += img.width
49
+ end
50
+ blocks.concat(split_block(b, img))
51
+ end
52
+ end
53
+
54
+ return {:width => width, :height => height}
55
+ end
56
+
57
+ def self.split_block(block, img)
58
+ blocks = []
59
+ img.x = block.x
60
+ img.y = block.y
61
+ if (block.width - img.width) * img.height > (block.height - img.height) * img.width
62
+ blocks.push Block.new(block.x + img.width, block.y, block.width - img.width, block.height) if block.width != img.width
63
+ blocks.push Block.new(block.x, block.y + img.height, img.width, block.height - img.height) if block.height != img.height
64
+ else
65
+ blocks.push Block.new(block.x + img.width, block.y, block.width - img.width, img.height) if block.width != img.width
66
+ blocks.push Block.new(block.x, block.y + img.height, block.width, block.height - img.height) if block.height != img.height
67
+ end
68
+ return blocks
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,156 @@
1
+ require 'ruby_sprites/image'
2
+ require 'ruby_sprites/block'
3
+
4
+ module RubySprites
5
+ module Packer
6
+ module HorizontalSplit
7
+
8
+ def self.pack(images, options = {})
9
+ width = 0
10
+ height = 0
11
+
12
+ images = images.dup
13
+
14
+ images.sort! do |a, b|
15
+ a.height <=> b.height
16
+ end
17
+
18
+ i = images.pop
19
+ width = i.width
20
+ height = i.height
21
+ i.x = 0
22
+ i.y = 0
23
+
24
+ images.sort! do |a, b|
25
+ a.area <=> b.area
26
+ end
27
+
28
+ block_heap = Heap.new {|a,b| b.area <=> a.area}
29
+
30
+ while !images.empty?
31
+ if block_heap.empty?
32
+ img = images.pop
33
+ if width == 0 && height == 0
34
+ img.x = 0
35
+ img.y = 0
36
+ width = img.width
37
+ height = img.height
38
+ else
39
+ if img.height > height
40
+ block_heap.insert(Block.new(0, height, width, img.height - height))
41
+ height = img.height
42
+ elsif img.height < height
43
+ block_heap.insert(Block.new(width, img.height, img.width, height - img.height))
44
+ end
45
+ img.x = width
46
+ img.y = 0
47
+ width += img.width
48
+ end
49
+ else
50
+ while !block_heap.empty? && !images.empty?
51
+ block = block_heap.remove
52
+ cur_img = nil
53
+ cur_exact = nil
54
+ images.each_index do |i|
55
+ cur_img = i if block.fits?(images[i])
56
+ cur_exact = i if block.fits?(images[i]) && (block.width == images[i].width || block.height == images[i].height)
57
+ end
58
+ if !cur_exact.nil?
59
+ img = images.delete_at(cur_exact)
60
+ img.x = block.x
61
+ img.y = block.y
62
+ split_block(block, img).each do |b|
63
+ block_heap.insert(b)
64
+ end
65
+ elsif !cur_img.nil?
66
+ img = images.delete_at(cur_img)
67
+ img.x = block.x
68
+ img.y = block.y
69
+ split_block(block, img).each do |b|
70
+ block_heap.insert(b)
71
+ end
72
+ else
73
+ # Nothing will fit in this block, we are throwing it out
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ return {:width => width, :height => height}
80
+ end
81
+
82
+ def self.split_block(block, img)
83
+ blocks = []
84
+ img.x = block.x
85
+ img.y = block.y
86
+ if (block.width - img.width) * img.height > (block.height - img.height) * img.width
87
+ blocks.push Block.new(block.x + img.width, block.y, block.width - img.width, block.height) if block.width != img.width
88
+ blocks.push Block.new(block.x, block.y + img.height, img.width, block.height - img.height) if block.height != img.height
89
+ else
90
+ blocks.push Block.new(block.x + img.width, block.y, block.width - img.width, img.height) if block.width != img.width
91
+ blocks.push Block.new(block.x, block.y + img.height, block.width, block.height - img.height) if block.height != img.height
92
+ end
93
+ return blocks
94
+ end
95
+
96
+ protected
97
+
98
+ class Heap
99
+ def initialize(&comparer)
100
+ @comparer = comparer
101
+ @heap = [0]
102
+ end
103
+
104
+ def insert(el)
105
+ pos = @heap.length
106
+ @heap.push(el)
107
+ new_pos = pos
108
+ while new_pos != 1 && @comparer.call(el, @heap[new_pos / 2]) < 0
109
+ new_pos /= 2
110
+ end
111
+
112
+ while(pos > new_pos)
113
+ @heap[pos] = @heap[pos / 2]
114
+ pos /= 2
115
+ end
116
+ @heap[pos] = el
117
+ end
118
+
119
+ def remove
120
+ return nil if @heap.length == 1
121
+ el = @heap[1]
122
+ shifter = @heap.pop
123
+ if @heap.length != 1
124
+ pos = 1
125
+ while(!@heap[pos * 2].nil?)
126
+ lesser_pos = pos * 2
127
+ lesser_pos += 1 if !@heap[pos * 2 + 1].nil? && @comparer.call(@heap[pos * 2 + 1], @heap[pos * 2]) < 0
128
+ if(@comparer.call(@heap[lesser_pos], shifter) < 0)
129
+ @heap[pos] = @heap[lesser_pos]
130
+ pos = lesser_pos
131
+ else
132
+ break
133
+ end
134
+ end
135
+
136
+ @heap[pos] = shifter
137
+ end
138
+ return el
139
+ end
140
+
141
+ def peek
142
+ return nil if @heap.length == 1
143
+ return @heap[1]
144
+ end
145
+
146
+ def empty?
147
+ return @heap.length == 1
148
+ end
149
+
150
+ def inspect
151
+ return @heap.to_s
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,22 @@
1
+ require 'ruby_sprites/image'
2
+
3
+ module RubySprites
4
+ module Packer
5
+ module HorizontalStack
6
+
7
+ def self.pack(images, options = {})
8
+ width = 0
9
+ height = 0
10
+ images.each do |img|
11
+ next unless img.exists?
12
+ img.x = width
13
+ img.y = 0
14
+ height = img.height if img.height > height
15
+ width += img.width
16
+ end
17
+ return {:width => width, :height => height}
18
+ end
19
+
20
+ end
21
+ end
22
+ end