has_image 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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
- commands.resize "#{size}^"
59
- commands.gravity "center"
60
- commands.extent size
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
@@ -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
- options[:thumbnails].each do |thumb_name, size|
145
- thumb = processor.resize(path, size)
146
- thumb.write(File.join(path_for(id), file_name_for(name, thumb_name)))
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
 
@@ -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 test_resize
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_image
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Norman Clarke