lib/morandi.rb in morandi-0.9.3 vs lib/morandi.rb in morandi-0.10.0
- old
+ new
@@ -3,262 +3,19 @@
require 'cairo'
require 'gdk_pixbuf2'
require 'pixbufutils'
require 'redeye'
+require 'morandi/image_processor'
require 'morandi/utils'
require 'morandi/image-ops'
require 'morandi/redeye'
module Morandi
- # Your code goes here...
- class ImageProcessor
- attr_reader :options, :pb
- attr_accessor :config
-
- def valid_jpeg?(filename)
- return false unless File.exist?(filename)
- return false unless File.size(filename) > 0
-
- type, _, _ = Gdk::Pixbuf.get_file_info(filename)
-
- type.name == 'jpeg'
- rescue
- false
- end
- def self.default_icc_path(path)
- "#{path}.icc.jpg"
- end
-
- def initialize(file, user_options, local_options={})
- @file = file
-
- user_options.keys.grep(/^path/).each { |k| user_options.delete(k) }
-
- # Give priority to user_options
- @options = (local_options || {}).merge(user_options || {})
- @local_options = local_options
-
- @scale_to = @options['output.max']
- @width, @height = @options['output.width'], @options['output.height']
-
- load_file = @file
- type, width, height = Gdk::Pixbuf.get_file_info(load_file)
-
- if type.name.eql?('jpeg')
- icc_file = local_options['path.icc'] || ImageProcessor.default_icc_path(@file)
- if valid_jpeg?(icc_file) || system("jpgicc", "-q97", @file, icc_file)
- load_file = icc_file
- end
- end
-
- if @scale_to
- @pb = Gdk::Pixbuf.new(load_file, @scale_to, @scale_to)
- @src_max = [width, height].max
- @actual_max = [@pb.width, @pb.height].max
- else
- @pb = Gdk::Pixbuf.new(load_file)
- @src_max = [@pb.width, @pb.height].max
- @actual_max = [@pb.width, @pb.height].max
- end
-
- @scale = @actual_max / @src_max.to_f
- end
-
- def process!
- # Apply Red-Eye corrections
- apply_redeye!
-
- # Apply contrast, brightness etc
- apply_colour_manipulations!
-
- # apply rotation
- apply_rotate!
-
- # apply crop
- apply_crop!
-
- # apply filter
- apply_filters!
-
- # add border
- apply_decorations!
-
- if @options['output.limit'] && @width && @height
- @pb = @pb.scale_max([@width, @height].max)
- end
-
- @pb
- end
-
- SHARPEN = [
- -1, -1, -1, -1, -1,
- -1, 2, 2, 2, -1,
- -1, 2, 8, 2, -1,
- -1, 2, 2, 2, -1,
- -1, -1, -1, -1, -1,
- ]
- BLUR = [
- 0, 1, 1, 1, 0,
- 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1,
- 0, 1, 1, 1, 0,
- ]
-
- def apply_colour_manipulations!
- #STDERR.puts "FILTER: #{options.inspect}"
- if options['brighten'].to_i.nonzero?
- brighten = [ [ 5 * options['brighten'], -100 ].max, 100 ].min
- #STDERR.puts([:brighten, brighten].inspect)
- @pb = PixbufUtils.brightness(@pb, brighten)
- end
- if options['gamma'] && (options['gamma'] != 1.0)
- @pb = PixbufUtils.gamma(@pb, options['gamma'])
- end
- if options['contrast'].to_i.nonzero?
- @pb = PixbufUtils.contrast(@pb, [ [ 5 * options['contrast'], -100 ].max, 100 ].min)
- end
- if options['sharpen'].to_i.nonzero?
- if options['sharpen'] > 0
- [options['sharpen'], 5].min.times do
- @pb = PixbufUtils.filter(@pb, SHARPEN, SHARPEN.inject(0, &:+))
- end
- elsif options['sharpen'] < 0
- [ (options['sharpen']*-1), 5].min.times do
- @pb = PixbufUtils.filter(@pb, BLUR, BLUR.inject(0, &:+))
- end
- end
- end
- end
-
- def apply_redeye!
- for eye in options['redeye'] || []
- @pb = Morandi::RedEye.tap_on(@pb, eye[0] * @scale, eye[1] * @scale)
- end
- end
-
- def angle
- a = options['angle'].to_i
- if a
- (360-a)%360
- else
- nil
- end
- end
-
- # modifies @pb with any applied rotation
- def apply_rotate!
- a = angle()
-
- unless (a%360).zero?
- @pb = @pb.rotate(a)
- end
-
- unless options['straighten'].to_f.zero?
- @pb = Morandi::Straighten.new(options['straighten'].to_f).call(nil, @pb)
- end
-
- @image_width = @pb.width
- @image_height = @pb.height
- end
-
- DEFAULT_CONFIG = {
- 'border-size-mm' => 5
- }
- def config_for(key)
- return options[key] if options && options.has_key?(key)
- return @config[key] if @config && @config.has_key?(key)
- DEFAULT_CONFIG[key]
- end
-
- #
- def apply_crop!
- crop = options['crop']
-
- if crop.nil? && config_for('image.auto-crop').eql?(false)
- return
- end
-
- if crop.is_a?(String) && crop =~ /^\d+,\d+,\d+,\d+/
- crop = crop.split(/,/).map(&:to_i)
- end
-
- crop = nil unless crop.is_a?(Array) && crop.size.eql?(4) && crop.all? { |i|
- i.kind_of?(Numeric)
- }
-
- # can't crop, won't crop
- return if @width.nil? && @height.nil? && crop.nil?
-
- if crop && @scale != 1.0
- crop = crop.map { |s| (s.to_f * @scale).floor }
- end
-
- crop ||= Morandi::Utils.autocrop_coords(@pb.width, @pb.height, @width, @height)
-
- @pb = Morandi::Utils.apply_crop(@pb, crop[0], crop[1], crop[2], crop[3])
- end
-
-
- def apply_filters!
- filter = options['fx']
-
- case filter
- when 'greyscale'
- op = Morandi::Colourify.new_from_hash('op' => filter)
- when 'sepia', 'bluetone'
- op = Morandi::Colourify.new_from_hash('op' => filter, 'alpha' => (0.85 * 255).to_i)
- else
- return
- end
- @pb = op.call(nil, @pb)
- end
-
- def apply_decorations!
- style, colour = options['border-style'], options['background-style']
-
- return if style.nil? or style.eql?('none')
- return if colour.eql?('none')
- colour ||= 'black'
-
- crop = options['crop']
- crop = crop.map { |s| (s.to_f * @scale).floor } if crop && @scale != 1.0
-
- op = Morandi::ImageBorder.new_from_hash(data={
- 'style' => style,
- 'colour' => colour || '#000000',
- 'crop' => crop,
- 'size' => [@image_width, @image_height],
- 'print_size' => [@width, @height],
- 'shrink' => true,
- 'border_size' => @scale * config_for('border-size-mm').to_i * 300 / 25.4 # 5mm at 300dpi
- })
-
- @pb = op.call(nil, @pb)
- end
-
- def write_to_png(fn, orientation=:any)
- pb = @pb
-
- case orientation
- when :landscape
- pb = @pb.rotate(90) if @pb.width < @pb.height
- when :portrait
- pb = @pb.rotate(90) if @pb.width > @pb.height
- end
- pb.save(fn, 'png')
- end
-
- def write_to_jpeg(fn, quality = 97)
- @pb.save(fn, 'jpeg', :quality => quality)
- end
-end
-
module_function
def process(file_in, options, out_file, local_options = {})
pro = ImageProcessor.new(file_in, options, local_options)
pro.process!
pro.write_to_jpeg(out_file)
end
-
end
+