lib/httpthumbnailer/plugin/thumbnailer.rb in httpthumbnailer-1.0.0 vs lib/httpthumbnailer/plugin/thumbnailer.rb in httpthumbnailer-1.1.0
- old
+ new
@@ -1,8 +1,31 @@
require 'RMagick'
require 'forwardable'
+# ImageMagick Image.mime_type is absolutely bunkers! It goes over file system to look for some strange files WTF?!
+# Also it cannot be used for thumbnails since they are not yet rendered to desired format
+# Here is stupid implementaiton
+module MetaData
+ def width
+ @image.columns
+ end
+
+ def height
+ @image.rows
+ end
+
+ def mime_type
+ #TODO: how do I do it better?
+ format = @format || @image.format
+ mime = case format
+ when 'JPG' then 'jpeg'
+ else format.downcase
+ end
+ "image/#{mime}"
+ end
+end
+
module Plugin
module Thumbnailer
class UnsupportedMethodError < ArgumentError
def initialize(method)
super("thumbnail method '#{method}' is not supported")
@@ -103,12 +126,23 @@
@image.use do |image|
yield self
end
end
- def_delegators :@image, :destroy!, :destroyed?, :mime_type
+ def_delegators :@image, :destroy!, :destroyed?
+ include MetaData
+
+ # We use base values since it might have been loaded with size hint and prescaled
+ def width
+ @image.base_columns
+ end
+
+ def height
+ @image.base_rows
+ end
+
# needs to be seen as @image when returned in replace block
def equal?(image)
super image or @image.equal? image
end
end
@@ -122,28 +156,20 @@
@quality = (options['quality'] or default_quality(format))
@quality &&= @quality.to_i
end
def data
- format = @format
- quality = @quality
- @image.to_blob do
- self.format = format
- self.quality = quality if quality
- end
- end
-
- def mime_type
- #@image.mime_type cannot be used since it is raw crated image
- #TODO: how do I do it better?
- mime = case @format
- when 'JPG' then 'jpeg'
- else @format.downcase
+ format = @format
+ quality = @quality
+ @image.to_blob do
+ self.format = format
+ self.quality = quality if quality
end
- "image/#{mime}"
end
+ include MetaData
+
private
def default_quality(format)
case format
when /png/i
@@ -160,11 +186,12 @@
include ClassLogging
extend Stats
def_stats(
:total_images_loaded,
- :total_images_prescaled,
+ :total_images_reloaded,
+ :total_images_downscaled,
:total_thumbnails_created,
:images_loaded,
:max_images_loaded,
:max_images_loaded_worker,
:total_images_created,
@@ -242,12 +269,12 @@
log.debug{"image event: #{which}, #{description}, #{id}, #{method}: loaded images: #{Service.stats.images_loaded}"}
end
end
def load(io, options = {})
- mw = options['max-width']
- mh = options['max-height']
+ mw = options[:max_width]
+ mh = options[:max_height]
if mw and mh
mw = mw.to_i
mh = mh.to_i
log.info "using max size hint of: #{mw}x#{mh}"
end
@@ -256,11 +283,11 @@
blob = io.read
old_memory_limit = nil
borrowed_memory_limit = nil
if options.member?(:limit_memory)
- borrowed_memory_limit = options[:limit_memory].borrow(options[:limit_memory].limit)
+ borrowed_memory_limit = options[:limit_memory].borrow(options[:limit_memory].limit, 'image magick')
old_memory_limit = set_limit(:memory, borrowed_memory_limit)
end
images = Magick::Image.from_blob(blob) do |info|
if mw and mh
@@ -268,44 +295,53 @@
define('jbig', 'size', "#{mw*2}x#{mh*2}")
end
end
image = images.first
- if image.columns > image.base_columns or image.rows > image.base_rows
+ if image.columns > image.base_columns or image.rows > image.base_rows and not options[:no_reload]
log.warn "input image got upscaled from: #{image.base_columns}x#{image.base_rows} to #{image.columns}x#{image.rows}: reloading without max size hint!"
images.each do |other|
other.destroy!
end
images = Magick::Image.from_blob(blob)
+ Service.stats.incr_total_images_reloaded
end
blob = nil
images.shift.replace do |image|
images.each do |other|
other.destroy!
end
log.info "loaded image: #{image.inspect}"
Service.stats.incr_total_images_loaded
+
+ # clean up the image
image.strip!
+ image.properties do |key, value|
+ log.debug "deleting user propertie '#{key}'"
+ image[key] = nil
+ end
+
+ image
end.replace do |image|
- if mw and mh
- f = image.find_prescale_factor(mw, mh)
+ if mw and mh and not options[:no_downscale]
+ f = image.find_downscale_factor(mw, mh)
if f > 1
- image = image.prescale(f)
- log.info "prescaled image by factor of #{f}: #{image.inspect}"
- Service.stats.incr_total_images_prescaled
+ image = image.downscale(f)
+ log.info "downscaled image by factor of #{f}: #{image.inspect}"
+ Service.stats.incr_total_images_downscaled
end
end
InputImage.new(image, @processing_methods)
end
rescue Magick::ImageMagickError => error
raise ImageTooLargeError, error if error.message =~ /cache resources exhausted/
raise UnsupportedMediaTypeError, error
ensure
if old_memory_limit
set_limit(:memory, old_memory_limit)
- options[:limit_memory].return(borrowed_memory_limit)
+ options[:limit_memory].return(borrowed_memory_limit, 'image magick')
end
end
end
def processing_method(method, &impl)
@@ -378,17 +414,17 @@
else
crop(gravity, ncols, nrows, true) if ncols != columns or nrows != rows
end
end
- def prescale(f)
+ def downscale(f)
sample(columns / f, rows / f)
end
- def find_prescale_factor(max_width, max_height, factor = 1)
+ def find_downscale_factor(max_width, max_height, factor = 1)
new_factor = factor * 2
if columns / new_factor > max_width * 2 and rows / new_factor > max_height * 2
- find_prescale_factor(max_width, max_height, factor * 2)
+ find_downscale_factor(max_width, max_height, factor * 2)
else
factor
end
end
end