Class: ImageProc

Inherits:
Object
  • Object
show all
Defined in:
lib/image_proc.rb

Overview

A simplistic interface to shell-based image processing. Pluggable, compact and WIN32-incompatible by design. Sort of like the Processors in attachment_fu but less. Less.

width, height = ImageProc.get_bounds("image.png")
destination_path = ImageProc.resize("image.png", "thumb.png", :width => 50. :height => 50)

The whole idea is: a backend does not have to support cropping (we don’t do it), it has only to be able to resize, and a backend should have 2 public methods. That’s the game.

Defined Under Namespace

Classes: DestinationLocked, Error, FormatUnsupported, InvalidOptions, MissingInput, MissingOutput, NoDestinationDir, NoOverwrites

Constant Summary collapse

VERSION =
'2.1.0'
HARMLESS =
[]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.detect_engineObject

Tries to detect the best engine available



44
45
46
47
48
49
50
51
52
# File 'lib/image_proc.rb', line 44

def detect_engine
  if ImageProcConvert.available?
    ImageProcConvert
  elsif RUBY_PLATFORM =~ /darwin/i
    ImageProcSips
  else
    raise "This system has no image processing facitilites that we can use. Time to compile RMagick or install a decent OS."
  end
end

.engineObject

Get the processor class currently assigned



41
# File 'lib/image_proc.rb', line 41

def engine; @@engine ||= detect_engine; @@engine; end

.engine=(kls) ⇒ Object

Assign a specific processor class



38
# File 'lib/image_proc.rb', line 38

def engine=(kls); @@engine = kls; end

.get_bounds(of) ⇒ Object

Qukckly get bounds of an image

ImageProc.get_bounds("/tmp/upload.tif") #=> [100, 120]


56
57
58
# File 'lib/image_proc.rb', line 56

def get_bounds(of)
  engine.new.get_bounds(File.expand_path(of))
end

.keep_quietObject

Run a block without warnings



27
28
29
30
31
32
33
34
35
# File 'lib/image_proc.rb', line 27

def keep_quiet
  o = $VERBOSE
  begin
    $VERBOSE = nil
    yield
  ensure
    $VERBOSE = o
  end
end

.method_missing(*args) ⇒ Object

:nodoc:



60
61
62
# File 'lib/image_proc.rb', line 60

def method_missing(*args) #:nodoc:
  engine.new.send(*args)
end

Instance Method Details

#fit_sizes(bounds, opts) ⇒ Object

Will fit the passed array of [input_width, input_heitght] proportionally and return an array of

recommended_width, recommended_height

honoring the following parameters:

:width - maximum width of the bounding rect :height - maximum height of the bounding rect

If you pass both the bounds will be fit into the rect having the :width and :height proportionally, downsizing the bounds if necessary. Useful for calculating needed size before resizing.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/image_proc.rb', line 160

def fit_sizes(bounds, opts)
  
  disallow_nil_values_in(opts)
  integerize_values_of(opts)
  
  ratio = bounds[0].to_f / bounds[1].to_f
  floats = case (opts.keys & [:height, :width])
    when []
      raise "The options #{opts.inspect} do not contain proper bounds"
    when [:width]
      desired_w = opts[:width]
      [desired_w, desired_w / ratio]
    when [:height]
      desired_h = opts[:height]
      [desired_h * ratio, desired_h]
    else # both, use reduction
      smallest_side = [opts[:width], opts[:height]].sort.shift
      if bounds[0] > bounds[1] # horizontal
        fit_sizes bounds, :width => smallest_side
      else
        fit_sizes bounds, :height => smallest_side
      end
  end

  # Prevent zero results 
  prevent_zeroes_in(floats)

  # Nudge output values to pixels so that we fit exactly    
  floats[0] = opts[:width] if (opts[:width] && floats[0] > opts[:width])
  floats[1] = opts[:height] if (opts[:height] && floats[1] > opts[:height])
  floats
end

#fit_sizes_with_crop(bounds, opts) ⇒ Object

Will fit the passed array of [input_width, input_heitght] to fill the whole rect and return an array of

recommended_width, recommended_height

honoring the following parameters:

:width - maximum width of the bounding rect :height - maximum height of the bounding rect

In contrast to fit_sizes it requires BOTH.

It’s recommended to clip the image which will be created with these bounds using CSS, as not all resizers support cropping - and besides it’s just too many vars.



203
204
205
206
207
208
# File 'lib/image_proc.rb', line 203

def fit_sizes_with_crop(bounds, opts)
  force_keys!(opts, :width, :height)
  scale = [opts[:width].to_f / bounds[0], opts[:height].to_f / bounds[1]].sort.pop
  result = [bounds[0] * scale, bounds[1] * scale]
  result.map{|e| e.round}
end

#resize(from_path, to_path, opts = {}) ⇒ Object

Resizes with specific options passed as a hash. It returns the destination path to the resized image. If you need to know the resulting size of the image just call a ImageProc.get_bounds on the result path

ImageProc.resize "/tmp/foo.jpg", "bla.jpg", :width => 120, :height => 30

Raises:



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/image_proc.rb', line 77

def resize(from_path, to_path, opts = {})
  if opts.is_a?(String)
    STDERR.puts "String argument to resize() is really deprecated"
    resize_with_geom_string(from_path, to_path, opts)
  end
  
  remove_nil_values!(opts)
  
  raise InvalidOptions, "The only allowed options are :width, :height and :fill" if (opts.keys - [:width, :height, :fill]).any?
  
  if opts[:width] && opts[:height] && opts[:fill]
    resize_fit_fill(from_path, to_path, opts[:width], opts[:height])
  elsif opts[:width] && opts[:height]
    resize_fit_both(from_path, to_path, opts[:width], opts[:height])
  elsif opts[:width]
    resize_fit_width(from_path, to_path, opts[:width])
  elsif opts[:height]
    resize_fit_height(from_path, to_path, opts[:height])
  else
    raise InvalidOptions, "Pass width, height or both"
  end
  raise MissingOutput unless File.exist?(to_path)
  
  return to_path
end

#resize_exact(from_path, to_path, to_width, to_height) ⇒ Object

Resize an image fitting the boundary exactly. Will stretch and squash.



109
110
111
112
113
114
# File 'lib/image_proc.rb', line 109

def resize_exact(from_path, to_path, to_width, to_height)
  validate_input_output_files(from_path, to_path)
  @target_w, @target_h = to_width, to_height
  resetting_state_afterwards { process_exact }
  to_path
end

#resize_fit_both(from_path, to_path, to_width, to_height) ⇒ Object Also known as: resize_fit

Resize an image fitting it into a rect.



117
118
119
120
121
122
# File 'lib/image_proc.rb', line 117

def resize_fit_both(from_path, to_path, to_width, to_height)
  validate_input_output_files(from_path, to_path)
  @target_w, @target_h = fit_sizes(get_bounds(from_path), :width => to_width, :height => to_height)
  resetting_state_afterwards { process_exact }
  to_path
end

#resize_fit_fill(from_path, to_path, width, height) ⇒ Object

Will resize the image so that it’s part always fills the rect of width and height It’s recommended to then simply use CSS overflow to crop off the edges which are not necessary. If you want more involved processing calculate the geometry directly.



145
146
147
148
149
150
# File 'lib/image_proc.rb', line 145

def resize_fit_fill(from_path, to_path, width, height)
  validate_input_output_files(from_path, to_path)
  @target_w, @target_h = fit_sizes_with_crop get_bounds(from_path), :height => height, :width => width
  resetting_state_afterwards { process_exact }
  to_path
end

#resize_fit_height(from_path, to_path, height) ⇒ Object

Same as resize_fit_width



135
136
137
138
139
140
# File 'lib/image_proc.rb', line 135

def resize_fit_height(from_path, to_path, height)
  validate_input_output_files(from_path, to_path)
  @target_w, @target_h = fit_sizes(get_bounds(from_path), :height => height)
  resetting_state_afterwards { process_exact }
  to_path
end

#resize_fit_square(from_path, to_path, square_side) ⇒ Object

Resize an image fitting the biggest side of it to the side of a square. A must for thumbs.



104
105
106
# File 'lib/image_proc.rb', line 104

def resize_fit_square(from_path, to_path, square_side)
  resize_fit_both(from_path, to_path, square_side, square_side)
end

#resize_fit_width(from_path, to_path, width) ⇒ Object

Resize an image fitting the biggest side of it to the side of a square. A must for thumbs.



126
127
128
129
130
131
132
# File 'lib/image_proc.rb', line 126

def resize_fit_width(from_path, to_path, width)
  validate_input_output_files(from_path, to_path)
  
  @target_w, @target_h = fit_sizes get_bounds(from_path), :width => width
  resetting_state_afterwards { process_exact }
  to_path
end

#resize_with_geom_string(from_path, to_path, geom_str) ⇒ Object

:nodoc:



65
66
67
68
69
70
# File 'lib/image_proc.rb', line 65

def resize_with_geom_string(from_path, to_path, geom_str) #:nodoc:
  w, h = geom_str.scan(/^(\d+)x(\d+)$/).to_a.flatten.map{|e| e.to_i }
  resize(from_path, to_path, :width => w, :height => h)
  result_w, result_h = get_bounds(to_path)
  [to_path, result_w, result_h]
end