lash-sprites 0.3.1

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.
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