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