has_image 0.1.1 → 0.1.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/CHANGELOG +5 -1
- data/lib/has_image/processor.rb +25 -3
- data/lib/has_image/storage.rb +25 -4
- data/lib/has_image.rb +4 -3
- data/test/processor_test.rb +17 -1
- data/test/storage_test.rb +7 -0
- metadata +1 -1
data/CHANGELOG
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
2008-07-28 Norman Clarke <norman@randomba.org>
|
2
2
|
|
3
|
+
* Added sorted thumbnail processing. This improves thumbnail generation
|
4
|
+
speed by about 25% for 4.5 meg jpegs with 5 thumbnails.
|
5
|
+
* Fixed broken resize for non-fixed-width thumbnails.
|
6
|
+
* Added check for bad geometry strings.
|
3
7
|
* Added dependencies and Rubyforge project to gemspec, updated docs.
|
4
|
-
|
8
|
+
|
5
9
|
2008-07-25 Norman Clarke <norman@randomba.org>
|
6
10
|
|
7
11
|
* First public release.
|
data/lib/has_image/processor.rb
CHANGED
@@ -8,6 +8,21 @@ module HasImage
|
|
8
8
|
attr_accessor :options
|
9
9
|
|
10
10
|
class << self
|
11
|
+
|
12
|
+
# Given a geometry string, return the maxium possible output dimensions.
|
13
|
+
# For example:
|
14
|
+
# area("50x50>") == 2500
|
15
|
+
def area(dimensions)
|
16
|
+
dimensions.split("x")[0].to_i * dimensions.split("x")[1].to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
# "The form of an {extended geometry
|
20
|
+
# string}[http://www.imagemagick.org/script/command-line-options.php?#resize] is
|
21
|
+
# <width>x<height>{+-}<xoffset>{+-}<yoffset>{%}{!}{<}{>}"
|
22
|
+
def geometry_string_valid?(string)
|
23
|
+
string =~ /\A[\d]*x[\d]*([+-][0-9][+-][0-9])?[%@!<>^]?\Z/
|
24
|
+
end
|
25
|
+
|
11
26
|
# Arg should be either a file, or a path. This runs ImageMagick's
|
12
27
|
# "identify" command and looks for an exit status indicating an error. If
|
13
28
|
# there is no error, then ImageMagick has identified the file as something
|
@@ -30,6 +45,7 @@ module HasImage
|
|
30
45
|
# format if necessary. The size should be a valid ImageMagick {geometry
|
31
46
|
# string}[http://www.imagemagick.org/script/command-line-options.php#resize].
|
32
47
|
def resize(file, size)
|
48
|
+
raise InvalidGeometryError.new unless Processor.geometry_string_valid?(size)
|
33
49
|
silence_stderr do
|
34
50
|
path = file.respond_to?(:path) ? file.path : file
|
35
51
|
file.close if file.respond_to?(:close) && !file.closed?
|
@@ -55,9 +71,15 @@ module HasImage
|
|
55
71
|
@image.combine_options do |commands|
|
56
72
|
commands.send("auto-orient".to_sym)
|
57
73
|
commands.strip
|
58
|
-
|
59
|
-
|
60
|
-
|
74
|
+
# Fixed-dimension images
|
75
|
+
if size =~ /\A[\d]*x[\d]*!?\Z/
|
76
|
+
commands.resize "#{size}^"
|
77
|
+
commands.gravity "center"
|
78
|
+
commands.extent size
|
79
|
+
# Non-fixed-dimension images
|
80
|
+
else
|
81
|
+
commands.resize "#{size}"
|
82
|
+
end
|
61
83
|
commands.quality options[:output_quality]
|
62
84
|
end
|
63
85
|
end
|
data/lib/has_image/storage.rb
CHANGED
@@ -105,6 +105,26 @@ module HasImage
|
|
105
105
|
options[:convert_to].to_s.downcase.gsub("jpeg", "jpg")
|
106
106
|
end
|
107
107
|
|
108
|
+
# Returns the options[:thumbnails] hash, coverted to an array and sorted
|
109
|
+
# by thumbnail area, highest to lowest. For example:
|
110
|
+
#
|
111
|
+
# options[:thumbnails] == {:a => "20x20", :b => "2x2", :c => "100x100"}
|
112
|
+
# sorted_thumbnails == [[:c, "100x100"], [:a, "20x20"], [:b, "2x2"]]
|
113
|
+
#
|
114
|
+
# This is done to speed up processing images with several thumbnails. Rather
|
115
|
+
# than create the thumbnail starting from the highest quality version each
|
116
|
+
# time, the next biggest thumbnail is used as the base image for its
|
117
|
+
# immediately smaller variant. For example, given an image with 3 thumbnails
|
118
|
+
# HasImage will use the 800x800 as the basis of the 500x500, and then the
|
119
|
+
# 500x500 as the basis of the 200x200, etc. My benchmarks showed that this
|
120
|
+
# will speed up processing by up to around 25% for a 4.5 meg JPEG with 5
|
121
|
+
# thumbnails.
|
122
|
+
def sorted_thumbnails
|
123
|
+
options[:thumbnails].to_a.sort do |b,a|
|
124
|
+
Processor.area(a[1]) <=> Processor.area(b[1])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
108
128
|
private
|
109
129
|
|
110
130
|
# File name, plus thumbnail suffix, plus extension. For example:
|
@@ -141,13 +161,14 @@ module HasImage
|
|
141
161
|
def install_thumbnails(id, name)
|
142
162
|
FileUtils.mkdir_p path_for(id)
|
143
163
|
path = File.join(path_for(id), file_name_for(name))
|
144
|
-
|
145
|
-
thumb = processor.resize(path,
|
146
|
-
|
164
|
+
sorted_thumbnails.each do |t|
|
165
|
+
thumb = processor.resize(path, t[1])
|
166
|
+
path = File.join(path_for(id), file_name_for(name, t[0]))
|
167
|
+
thumb.write(path)
|
147
168
|
thumb.tempfile.close!
|
148
169
|
end
|
149
170
|
end
|
150
|
-
|
171
|
+
|
151
172
|
# Get the full path for the id. For example:
|
152
173
|
#
|
153
174
|
# /var/sites/example.org/production/public/photos/0000/0001
|
data/lib/has_image.rb
CHANGED
@@ -64,9 +64,10 @@ require 'has_image/view_helpers'
|
|
64
64
|
module HasImage
|
65
65
|
|
66
66
|
class ProcessorError < StandardError ; end
|
67
|
-
class StorageError < StandardError ; end
|
68
|
-
class FileTooBigError < StorageError ; end
|
69
|
-
class FileTooSmallError < StorageError ; end
|
67
|
+
class StorageError < StandardError ; end
|
68
|
+
class FileTooBigError < StorageError ; end
|
69
|
+
class FileTooSmallError < StorageError ; end
|
70
|
+
class InvalidGeometryError < ProcessorError ; end
|
70
71
|
|
71
72
|
class << self
|
72
73
|
|
data/test/processor_test.rb
CHANGED
@@ -13,6 +13,10 @@ class StorageTest < Test::Unit::TestCase
|
|
13
13
|
return @temp_file
|
14
14
|
end
|
15
15
|
|
16
|
+
def test_area
|
17
|
+
assert_equal 2500, HasImage::Processor.area("50x50>")
|
18
|
+
end
|
19
|
+
|
16
20
|
def test_detect_valid_image
|
17
21
|
assert HasImage::Processor.valid?(File.dirname(__FILE__) + "/../test_rails/fixtures/image.jpg")
|
18
22
|
end
|
@@ -28,12 +32,24 @@ class StorageTest < Test::Unit::TestCase
|
|
28
32
|
def test_detect_invalid_image_from_tmp_file
|
29
33
|
assert !HasImage::Processor.valid?(temp_file("bad_image.jpg"))
|
30
34
|
end
|
35
|
+
|
36
|
+
def test_resize_with_invalid_geometry
|
37
|
+
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
38
|
+
assert_raises HasImage::InvalidGeometryError do
|
39
|
+
@processor.resize(temp_file("image.jpg"), "bad_geometry")
|
40
|
+
end
|
41
|
+
end
|
31
42
|
|
32
|
-
def
|
43
|
+
def test_resize_fixed
|
33
44
|
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
34
45
|
assert @processor.resize(temp_file("image.jpg"), "100x100")
|
35
46
|
end
|
36
47
|
|
48
|
+
def test_resize_unfixed
|
49
|
+
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
50
|
+
assert @processor.resize(temp_file("image.jpg"), "1024x768>")
|
51
|
+
end
|
52
|
+
|
37
53
|
def test_resize_and_convert
|
38
54
|
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
39
55
|
assert @processor.resize(temp_file("image.png"), "100x100")
|
data/test/storage_test.rb
CHANGED
@@ -16,6 +16,13 @@ class StorageTest < Test::Unit::TestCase
|
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
19
|
+
def test_sorted_thumbnails
|
20
|
+
thumbs = {:a => "20x20", :b => "2x2", :c => "100x100"}
|
21
|
+
sorted = [[:c, "100x100"], [:a, "20x20"], [:b, "2x2"]]
|
22
|
+
@storage = HasImage::Storage.new(default_options.merge(:thumbnails => thumbs))
|
23
|
+
assert_equal sorted, @storage.send(:sorted_thumbnails)
|
24
|
+
end
|
25
|
+
|
19
26
|
def test_partitioned_path
|
20
27
|
assert_equal(["0001", "2345"], HasImage::Storage.partitioned_path("12345"))
|
21
28
|
end
|