image_voodoo 0.8.8 → 0.8.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10db8a256e32e37a28b94c9246049f7029484a49
4
- data.tar.gz: 27ec82a19aff0bc3171a2c0bff8338c1c7ef19e6
3
+ metadata.gz: 525eaa36345ce66a917c101be73299b04e2800cb
4
+ data.tar.gz: e851fe90c983c411388d7b1e37ef0c2e323f677b
5
5
  SHA512:
6
- metadata.gz: 9bf960b9cd108f06ea22ba69a5f01f7d5ee3ca10dec63fe7a79cf6d2fe986f9a6753ef1969c8eabfa95af8509e0b19211b6ea7c0718d6106c42d998e255e6e36
7
- data.tar.gz: 27e8b1a524c25f7a72b3b15c3a9e37e4bf983111410a8be64d78b45c55ce133ed185ec5cf47ab527b565828d106515fdf053ccc41ee0298b1b1b6df75fc923f3
6
+ metadata.gz: 02453c98b8e64606ecb01834cebd6d04ba96212377308ca3f4a36f2ab91ae79db59dee0ca1b8056d912b3e66eea71da20d120fb81935d98f6d3a1550e3e57b17
7
+ data.tar.gz: 204639d91890c6525a6bae30fd1db9fd61f999187576e5213ac064bc1b8b29e57d3b7412b4ae691e6c1caa9922bdf0659f5535c49a0ebfed9344af5721bdb4db
@@ -0,0 +1,115 @@
1
+ # Experiment to tweak source based on what rubocop wanted me to. I basically
2
+ # gave up and made the following exceptions. Personally, I find the tool did
3
+ # find many interesting/questionable code Qs in the project but it is soooo
4
+ # opinionated in pretty unimportant ways (like who cares how I write my
5
+ # comments, or why can't I use kind_of? or why give me a fail for a less
6
+ # readable but more performant pattern).
7
+ #
8
+ # I can obviously tweak rules (like below) but I keep hearing of people pulling
9
+ # their hair out over all the unimportant rules; I question why this project is
10
+ # "all-in" on it's rules versus leaving most of the style rules as some preset
11
+ # profiles people can opt in to.
12
+ #
13
+ # So I am committing this and I will try and run this every now and then to
14
+ # see what it thinks about what I wrote but you will not see this as a
15
+ # requirement for commits since I think by the time I configured it to my
16
+ # liking I could have finished implementing this library. Perhaps if I
17
+ # continue using this here and there I will end up with a profile I find
18
+ # acceptable? I did find value but all the piddly shit just made me angry.
19
+ # I will reflect on it some more...
20
+
21
+ # This is a Java AWT call. It is not what the tool thinks it is...which
22
+ # might be an issue for users interacting with Java from JRuby.
23
+ Lint/UselessSetterCall:
24
+ Exclude:
25
+ - 'lib/image_voodoo/awt.rb'
26
+
27
+ # This is just impossible for them to call properly. I am doing something
28
+ # like rotate_impl and it is a bunch of reasonably simple math. If you look
29
+ # for this transformation in a text book it will look like this code. Putting
30
+ # a bunch of math within different methods would get the score down but
31
+ # it would not help readability. (addendum: I did try and break it apart more
32
+ # and never reached 15 but got closer. I felt this rule is neat but the
33
+ # score really depends on what it is.
34
+ Metrics/AbcSize:
35
+ Max: 16.5
36
+
37
+ # This is triggered for correct_orientation_impl. I could put these transforms
38
+ # in a table and then execute lambdas for each orientation but this is much
39
+ # more clear.
40
+ Metrics/CyclomaticComplexity:
41
+ Max: 9
42
+
43
+ # A bunch of fields in a hash are basically generated data. Correcting them
44
+ # for column seems much too pedantic. I guess exclude is the right thing for
45
+ # an unusual file?
46
+ Metrics/LineLength:
47
+ Max: 132
48
+ Exclude:
49
+ - 'lib/image_voodoo/metadata.rb'
50
+
51
+ # Metadata classes have data in them. awt.rb is big and perhaps could be
52
+ # shrunk but it is not a hard file to navigate and the cleaving points are
53
+ # not super obvious. These sorts of rules feel very arbitrary and if I have
54
+ # n highly related things and they do not fit into a more restrictive
55
+ # taxonomy why would the file being longer matter? I do understand the
56
+ # motivation here but as a default rule this feels wrong to me.
57
+ Metrics/ClassLength:
58
+ Max: 250
59
+
60
+ # I do not find this very useful. There is no performance difference and
61
+ # sometimes I want to highlight this string does not involve interpolation.
62
+ # Other times it is not worth pointing out.
63
+ Style/StringLiterals:
64
+ Enabled: false
65
+
66
+ # require 'english' is a step too far for $! which is so baked into my
67
+ # head I do not want to change :)
68
+ Style/SpecialGlobalVars:
69
+ Enabled: false
70
+
71
+ # I am grouping math and using lack of whitespace for separation.
72
+ Style/SpaceAroundOperators:
73
+ Enabled: false
74
+
75
+ # I prefer tight assignment for opt args.
76
+ Style/SpaceAroundEqualsInParameterDefault:
77
+ Enabled: false
78
+
79
+ # Java methods which override or implement Java method names cannot be switched
80
+ # to snake case. Do we really need this as a rule anyways? I have never seen
81
+ # a Rubyist do this as a preferred style?
82
+ Style/MethodName:
83
+ Enabled: false
84
+
85
+ # bin/image_voodoo main options block.
86
+ Metrics/BlockLength:
87
+ Exclude:
88
+ - 'bin/image_voodoo'
89
+
90
+ # casecmp for this case seems like it is much less readable in a place where
91
+ # performance could never matter (a 3-4 char downcase before processing an
92
+ # image :) ). This could end up being important somewhere but as a default
93
+ # on it feels weird since I find it less readable.
94
+ Performance/Casecmp:
95
+ Exclude:
96
+ - 'lib/image_voodoo/awt.rb'
97
+
98
+ # Hash rocket looks much more natural in a rakefile for its deps.
99
+ Style/HashSyntax:
100
+ Exclude:
101
+ - 'Rakefile'
102
+
103
+ # Forget it. I do parallel assignment and you will have to peel it out of
104
+ # my cold dead hands.
105
+ Style/ParallelAssignment:
106
+ Enabled: false
107
+
108
+ # FIXME: consider keywords for shapes.
109
+ # I might switch these to keyword args if I ever revisit shapes support.
110
+ # In general lots of params do suck and are hard to remember. This library
111
+ # still is supposed to work in 1.8 but I can probably soon major rev this
112
+ # and switch over to keywords.
113
+ Metrics/ParameterLists:
114
+ Exclude:
115
+ - 'lib/image_voodoo/awt/shapes.rb'
@@ -1,2 +1,9 @@
1
+ language: ruby
2
+
3
+ sudo: false
4
+
1
5
  rvm:
2
6
  - jruby-19mode
7
+
8
+ before_script:
9
+ - git clone https://github.com/drewnoakes/metadata-extractor-images.git ../metadata-extractor-images
data/Rakefile CHANGED
@@ -4,10 +4,10 @@ Bundler::GemHelper.install_tasks
4
4
 
5
5
  task :default => :test
6
6
 
7
- desc "Run tests"
7
+ desc 'Run tests'
8
8
  task :test do
9
9
  Rake::TestTask.new do |t|
10
- t.libs << "lib:vendor"
10
+ t.libs << 'lib:vendor'
11
11
  t.test_files = FileList['test/test*.rb']
12
12
  end
13
13
  end
@@ -7,101 +7,91 @@ actions = []
7
7
  images = []
8
8
  original_image = nil
9
9
 
10
- opts = OptionParser.new do |opts|
11
- opts.banner = "Usage: image_voodoo [actions] image_file"
12
- opts.separator "Perform some actions on a source image."
13
-
14
- opts.separator ""
15
- opts.separator "Actions are meant to be chained. Examples:"
16
- opts.separator " # Print the dimensions"
17
- opts.separator " image_voodoo --dim small.jpg"
18
- opts.separator ""
19
- opts.separator " # Convert to a thumbnail, preview, and then save the result"
20
- opts.separator " image_voodoo --thumbnail 50 --preview --save thumb.png large.jpg"
21
- opts.separator ""
22
- opts.separator " # Convert source to 3 thumbnails, showing dimensions and"
23
- opts.separator " # previewing along the way"
24
- opts.separator " image_voodoo --dim --resize 50x50 --dim --preview --save t1.jpg"
25
- opts.separator " --pop --resize 40x40 --dim --preview --save t2.jpg"
26
- opts.separator " --pop --resize 30x30 --dim --preview --save t3.jpg image.jpg"
27
-
28
- opts.separator ""
29
- opts.separator "Actions:"
30
-
31
- opts.on("-a", "--alpha color_value", "Make color transparent in image") do |c|
32
- if c !~ /[[:xdigit:]]{6,6}/
33
- opts.usage "color_value is rrggbb in hexidecimal format"
34
- end
35
- actions << lambda {|img| img.alpha(c) }
36
- end
37
-
38
- opts.on("-b", "--brightness SCALE,OFFSET", "Adjust brightness") do |args|
39
- scale, offset = args.split(/,/).map {|v| v.to_f}
40
- opts.usage "You need to specify proper scale and offset" unless scale && offset
41
- actions << lambda {|img| img.adjust_brightness(scale, offset) }
42
- end
43
-
44
- opts.on("-B", "--border WIDTH,COLOR,STYLE", "Add a simple border") do |args|
10
+ opts = OptionParser.new do |o|
11
+ o.banner = 'Usage: image_voodoo [actions] image_file'
12
+ o.separator 'Perform actions/transformations on an image.'
13
+ o.separator ''
14
+ o.separator 'Examples:'
15
+ o.separator ' image_voodoo --dim small.jpg # Print the dimensions'
16
+ o.separator ''
17
+ o.separator ' # Make a thumbnail, preview it, and then save it.'
18
+ o.separator ' image_voodoo --thumbnail 50 --preview --save thumb.png large.jpg'
19
+ o.separator ''
20
+ o.separator ' # Make 2 thumbnails, showing dimensions and previewing them'
21
+ o.separator ' image_voodoo --dim --resize 50x50 --dim --preview --save t1.jpg'
22
+ o.separator ' --pop --resize 40x40 --dim --preview --save t2.jpg image.jpg'
23
+ o.separator ''
24
+ o.separator 'Actions:'
25
+
26
+ o.on('-a', '--alpha rrggbb', 'Make color transparent in image') do |c|
27
+ o.usage 'rrggbb is in hexidecimal format' if c !~ /[[:xdigit:]]{6,6}/
28
+ actions << ->(img) { img.alpha(c) }
29
+ end
30
+
31
+ o.on('-b', '--brightness SCALE,OFFSET', 'Adjust brightness') do |args|
32
+ scale, offset = args.split(/,/).map(&:to_f)
33
+ o.usage 'You need to specify proper scale and offset' unless scale && offset
34
+ actions << ->(img) { img.adjust_brightness(scale, offset) }
35
+ end
36
+
37
+ o.on('-B', '--border WIDTH,COLOR,STYLE', 'Add a simple border') do |args|
45
38
  width, color, style = args.split(/,/)
46
- options = {:width => width, :color => color, :style => style }
47
-
48
- actions << lambda {|img| img.add_border(options) }
39
+ options = { width: width, color: color, style: style }
40
+ actions << ->(img) { img.add_border(options) }
49
41
  end
50
42
 
51
- opts.on("-d", "--dimensions", "Print the image dimensions") do
52
- actions << lambda {|img| puts "#{img.width}x#{img.height}"; img }
43
+ o.on('-d', '--dimensions', 'Print the image dimensions') do
44
+ actions << ->(img) { img.tap { puts "#{img.width}x#{img.height}" } }
53
45
  end
54
46
 
55
- opts.on("-g", "--greyscale", "Convert image to greyscale") do
56
- actions << lambda {|img| img.greyscale }
47
+ o.on('-g', '--greyscale', 'Convert image to greyscale') do
48
+ actions << ->(img) { img.greyscale }
57
49
  end
58
50
 
59
- opts.on("-h", "--flip_horizontally") do
60
- actions << lambda {|img| img.flip_horizontally }
51
+ o.on('-h', '--flip_horizontally') do
52
+ actions << ->(img) { img.flip_horizontally }
61
53
  end
62
54
 
63
- opts.on("-m", "--metadata") do
64
- actions << lambda {|img| puts img.metadata }
55
+ o.on('-m', '--metadata') do
56
+ actions << ->(img) { img.tap { puts img.metadata } }
65
57
  end
66
58
 
67
- opts.on("-n", "--negative", "Make a negative out of the image") do
68
- actions << lambda {|img| img.negative }
59
+ o.on('-n', '--negative', 'Make a negative out of the image') do
60
+ actions << ->(img) { img.negative }
69
61
  end
70
62
 
71
- opts.on("-o", "--orient", "Rotate image to orient it based on metadata") do
72
- actions << lambda {|img| img.correct_orientation }
63
+ o.on('-o', '--orient', 'Rotate image to orient it based on metadata') do
64
+ actions << ->(img) { img.correct_orientation }
73
65
  end
74
66
 
75
- opts.on("-q", "--quality 0..1", Float, "Set % of quality for lossy compression") do |quality|
76
- actions << lambda {|img| img.quality(quality) }
67
+ o.on('-q', '--quality 0..1', Float, 'Set % of quality for lossy compression') do |quality|
68
+ actions << ->(img) { img.quality(quality) }
77
69
  end
78
70
 
79
- opts.on("-R", "--rotate 0..360", Float, "Set angle to rotate image") do |angle|
80
- actions << lambda {|img| img.rotate(angle.to_f) }
71
+ o.on('-R', '--rotate 0..360', Float, 'Set angle to rotate image') do |angle|
72
+ actions << ->(img) { img.rotate(angle.to_f) }
81
73
  end
82
74
 
83
- opts.on("-r", "--resize WIDTHxHEIGHT", "Create a new image with the specified", "dimensions") do |dim|
84
- width, height = dim.split(/x/i).map {|v| v.to_i}
85
- opts.usage "You need to specify proper dimensions" unless width && width > 0 && height && height > 0
86
- actions << lambda {|img| img.resize(width,height) }
75
+ o.on('-r', '--resize WIDTHxHEIGHT', 'Make a new resized image') do |dim|
76
+ width, height = dim.split(/x/i).map(&:to_i)
77
+ o.usage 'You need to specify proper dimensions' unless width && width > 0 && height && height > 0
78
+ actions << ->(img) { img.resize(width, height) }
87
79
  end
88
80
 
89
-
90
- opts.on("-s", "--save FILENAME", "Save the results to a new file") do |f|
91
- actions << lambda {|img| img.save(f); img }
81
+ o.on('-s', '--save FILENAME', 'Save the results to a new file') do |f|
82
+ actions << ->(img) { img.tap { img.save(f) } }
92
83
  end
93
84
 
94
- opts.on("-t", "--thumbnail SIZE", Integer, "Create a thumbnail of the given size") do |size|
95
- actions << lambda {|img| img.thumbnail(size) }
85
+ o.on('-t', '--thumbnail SIZE', Integer, 'Create a thumbnail of the given size') do |size|
86
+ actions << ->(img) { img.thumbnail(size) }
96
87
  end
97
88
 
98
- opts.on("-v", "--flip_vertically") do
99
- actions << lambda {|img| img.flip_vertically }
100
- end
101
-
102
- opts.on("-p", "--preview", "Preview the image. Close the frame window",
103
- "to continue, or quit the application to", "abort the action pipeline") do
89
+ o.on('-v', '--flip_vertically') { actions << ->(img) { img.flip_vertically } }
104
90
 
91
+ o.on('-p', '--preview',
92
+ 'Preview the image. Close the frame window',
93
+ 'to continue, or quit the application to',
94
+ 'abort the action pipeline') do
105
95
  headless = false
106
96
  actions << lambda do |img|
107
97
  done = false
@@ -111,36 +101,33 @@ opts = OptionParser.new do |opts|
111
101
  end
112
102
  end
113
103
 
114
- opts.on("--push", "Save the current image to be popped later") do
115
- actions << lambda {|img| images << img; img }
104
+ o.on('--push', 'Save the current image to be popped later') do
105
+ actions << ->(img) { img.tap { images << img } }
116
106
  end
117
107
 
118
- opts.on("--pop", "Revert back to the previous saved image", "or the original source image") do
119
- actions << lambda {|img| images.pop || original_image }
108
+ o.on('--pop', 'Revert back to the previous image') do
109
+ actions << ->() { images.pop || original_image }
120
110
  end
121
111
 
122
- opts.on("-f", "--format", "Print the image format") do
123
- actions << lambda {|img| puts img.format; img }
112
+ o.on('-f', '--format', 'Print the image format') do
113
+ actions << ->(img) { img.tap { img.format } }
124
114
  end
125
115
 
126
- opts.on_tail("-h", "--help", "Show this message") do
127
- puts opts
128
- exit 0
129
- end
116
+ o.on_tail('-h', '--help', 'Show this message') { o.usage }
130
117
 
131
- def opts.usage(msg)
132
- puts msg
118
+ def o.usage(msg=nil)
119
+ puts msg if msg
133
120
  puts self
134
121
  exit 1
135
122
  end
136
123
  end
137
124
  opts.parse!(ARGV)
138
- opts.usage("You need to supply a source image filename.") unless ARGV.first
139
- opts.usage("You need to supply one or more actions.") unless actions.size > 0
125
+ opts.usage('You need to supply a source image filename.') unless ARGV.first
126
+ opts.usage('You need to supply one or more actions.') if actions.empty?
140
127
 
141
128
  # For this binstub we only want to load non-headless if we are using
142
129
  # the preview feature. top of See lib/image_voodoo.rb for more info...
143
- class ImageVoodoo; NEEDS_HEAD = true; end unless headless
130
+ require 'image_voodoo/needs_head' unless headless
144
131
 
145
132
  require 'image_voodoo'
146
133
  file_name = ARGV.first
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "image_voodoo/version"
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'image_voodoo/version'
4
5
 
5
6
  Gem::Specification.new do |s|
6
7
  s.name = 'image_voodoo'
@@ -12,11 +13,11 @@ Gem::Specification.new do |s|
12
13
  s.summary = 'Image manipulation in JRuby with ImageScience compatible API'
13
14
  s.description = 'Image manipulation in JRuby with ImageScience compatible API'
14
15
 
15
- s.rubyforge_project = "image_voodoo"
16
+ s.rubyforge_project = 'image_voodoo'
16
17
 
17
18
  s.files = `git ls-files`.split("\n")
18
19
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ["lib", "vendor"]
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
21
+ s.require_paths = %w[lib vendor]
21
22
  s.has_rdoc = true
22
23
  end
@@ -1,3 +1,3 @@
1
1
  require 'image_voodoo'
2
2
  # HA HA...let the pin-pricking begin
3
- ImageScience = ImageVoodoo
3
+ ImageScience = ImageVoodoo
@@ -5,10 +5,7 @@
5
5
  unless defined? ImageVoodoo::NEEDS_HEAD
6
6
  java.lang.System.set_property 'java.awt.headless', 'true'
7
7
  end
8
-
9
8
 
10
- ##
11
- #
12
9
  # = ImageVoodoo
13
10
  # == Description
14
11
  #
@@ -33,7 +30,6 @@ end
33
30
  #
34
31
  # img = ImageVoodoo.with_image(ARGV[0])
35
32
  # negative_img = img.negative
36
- #
37
33
  class ImageVoodoo
38
34
  attr_accessor :quality
39
35
 
@@ -41,16 +37,15 @@ class ImageVoodoo
41
37
 
42
38
  JFile = java.io.File
43
39
 
44
- ##
45
40
  # FIXME: This has an issue when used in test/unit where the classcastexception
46
41
  # is throwing the stack trace to output. This does not happen when used
47
42
  # directly. Not sure....
48
43
  # gae and awt define the technology-specific methods and more importantly
49
44
  # all the *_impl methods which you will see referenced in this file.
50
45
  begin
51
- require 'image_voodoo/gae'
46
+ require 'image_voodoo/gae'
52
47
  rescue
53
- require 'image_voodoo/awt'
48
+ require 'image_voodoo/awt'
54
49
  end
55
50
 
56
51
  def initialize(io, src, format=nil)
@@ -58,127 +53,107 @@ class ImageVoodoo
58
53
  @quality = nil # nil means no specific quality ever specified
59
54
  end
60
55
 
61
- ##
62
- #
56
+ # Gets RGB value within the source image at [x, y]. If using AWT backend
57
+ # then consider using color_at as this is a Java signed int value of an
58
+ # unsigned value.
59
+ def pixel(x, y)
60
+ @src.getRGB(x, y)
61
+ end
62
+
63
63
  # Adjusts the brightness of each pixel in image by the following formula:
64
64
  # new_pixel = pixel * scale + offset
65
- #
66
65
  def adjust_brightness(scale, offset)
67
66
  image = guard { adjust_brightness_impl(scale, offset) }
68
67
  block_given? ? yield(image) : image
69
68
  end
70
69
 
71
- ##
72
- #
73
70
  # Converts rgb hex color value to an alpha value an yields/returns the new
74
71
  # image.
75
- #
76
72
  def alpha(rgb)
77
73
  target = guard { alpha_impl(rgb) }
78
74
  block_given? ? yield(target) : target
79
75
  end
80
76
 
81
- ##
82
- #
83
77
  # Get current image bytes as a String using provided format. Format parameter
84
78
  # is the informal name of an image type - for instance,
85
79
  # "bmp" or "jpg". If the backend is AWT the types available are listed in
86
80
  # javax.imageio.ImageIO.getWriterFormatNames()
87
- #
88
81
  def bytes(format)
89
82
  java_bytes = guard { bytes_impl(format) }
90
83
  String.from_java_bytes java_bytes
91
84
  end
92
85
 
93
- ##
94
86
  # If current image was taken by a phone it might save the orientation
95
87
  # in format it was physically taken and added IFD0 Orientation information
96
88
  # instead of rotating it. This method will perform that rotation based
97
89
  # on Orientation metadata.
98
- #
99
90
  def correct_orientation
100
91
  target = guard { correct_orientation_impl }
101
92
  block_given? ? yield(target) : target
102
93
  end
103
94
 
104
- ##
105
- #
106
95
  # Creates a square thumbnail of the image cropping the longest edge to
107
96
  # match the shortest edge, resizes to size, and yields/returns the new image.
108
- #
109
97
  def cropped_thumbnail(size)
110
- l, t, r, b, half = 0, 0, width, height, (width - height).abs / 2
111
- l, r = half, half + height if width > height
112
- t, b = half, half + width if height > width
113
-
98
+ l, t, r, b = calculate_thumbnail_dimentions
114
99
  target = with_crop(l, t, r, b).thumbnail(size)
115
100
  block_given? ? yield(target) : target
116
101
  end
117
102
 
118
- ##
119
- #
103
+ def calculate_thumbnail_dimensions
104
+ half = (width - height).abs / 2
105
+ if width > height
106
+ [half, 0, half + height, height]
107
+ else
108
+ [0, half, width, half + width]
109
+ end
110
+ end
111
+ private :calculate_thumbnail_dimensions
112
+
120
113
  # Flips the image horizontally and yields/returns the new image.
121
- #
122
114
  def flip_horizontally
123
115
  target = guard { flip_horizontally_impl }
124
116
  block_given? ? yield(target) : target
125
117
  end
126
118
 
127
- ##
128
- #
129
119
  # Flips the image vertically and yields/returns the new image.
130
- #
131
120
  def flip_vertically
132
121
  target = guard { flip_vertically_impl }
133
122
  block_given? ? yield(target) : target
134
123
  end
135
124
 
136
- ##
137
- #
138
125
  # Creates a grayscale version of image and yields/returns the new image.
139
- #
140
126
  def greyscale
141
127
  target = guard { greyscale_impl }
142
128
  block_given? ? yield(target) : target
143
129
  end
144
- alias_method :grayscale, :greyscale
130
+ alias grayscale greyscale
145
131
 
146
- ##
147
- #
148
132
  # Extracts metadata from an image.
149
- #
150
133
  def metadata
151
134
  guard { metadata_impl }
152
135
  end
153
136
 
154
- ##
155
- #
156
137
  # Creates a negative and yields/returns the new image.
157
- #
158
138
  def negative
159
139
  target = guard { negative_impl }
160
140
  block_given? ? yield(target) : target
161
141
  end
162
142
 
163
- ##
164
- #
165
143
  # Set quality you want resulting image to be once you save or extract
166
144
  # bytes for the image. Note: This will only work for lossy image
167
145
  # formats like PNG of JPEG. For others it will be ignored.
168
146
  def quality(amount)
169
147
  if amount < 0.0 || amount > 1.0
170
- raise ArgumentError.new "Quality must be between 0.0 and 1.0"
148
+ raise ArgumentError, 'Quality must be between 0.0 and 1.0'
171
149
  end
172
150
 
173
- target = self.dup
151
+ target = dup
174
152
  target.quality = amount
175
153
  block_given? ? yield(target) : target
176
154
  end
177
155
 
178
- ##
179
- #
180
156
  # Resizes the image to width and height and yields/returns the new image.
181
- #
182
157
  def resize(width, height)
183
158
  target = guard { resize_impl(width, height) }
184
159
  block_given? ? yield(target) : target
@@ -186,135 +161,108 @@ class ImageVoodoo
186
161
  raise ArgumentError, ne.message
187
162
  end
188
163
 
189
- ##
190
- #
191
164
  # Rotates the image by angle (specified in degrees).
192
- #
193
165
  def rotate(angle)
194
- target = guard { rotate_impl(angle) }
166
+ target = guard { rotate_impl(to_radians(angle)) }
195
167
  block_given? ? yield(target) : target
196
168
  end
197
169
 
198
- ##
199
- #
200
170
  # Saves the image out to path. Changing the file extension will convert
201
171
  # the file type to the appropriate format.
202
- #
203
172
  def save(file)
204
173
  format = File.extname(file)
205
- return false if format == ""
174
+ return false if format == ''
206
175
  format = format[1..-1].downcase
207
176
  guard { save_impl(format, JFile.new(file)) }
208
177
  true
209
178
  end
210
179
 
211
- ##
212
- #
213
180
  # Resize (scale) the current image by the provided ratio and yield/return
214
181
  # the new image.
215
- #
216
182
  def scale(ratio)
217
183
  new_width, new_height = (width * ratio).to_i, (height * ratio).to_i
218
184
  target = resize(new_width, new_height)
219
185
  block_given? ? yield(target) : target
220
186
  end
221
187
 
222
- ##
223
- #
224
188
  # Creates a proportional thumbnail of the image scaled so its longest
225
189
  # edge is resized to size and yields/returns the new image.
226
- #
227
190
  def thumbnail(size)
228
191
  target = scale(size.to_f / (width > height ? width : height))
229
192
  block_given? ? yield(target) : target
230
193
  end
231
194
 
232
- ##
233
- #
234
195
  # Crops an image to left, top, right, and bottom and then yields/returns the
235
196
  # new image.
236
- #
237
197
  def with_crop(left, top, right, bottom)
238
198
  image = guard { with_crop_impl(left, top, right, bottom) }
239
199
  block_given? ? yield(image) : image
240
200
  end
241
201
 
242
- ##
243
- #
202
+ # Creates a new (empty) image with a file name specified.
203
+ def self.new_image(width, height, file_name)
204
+ image = guard { new_image_impl(width, height, file_name) }
205
+ block_given? ? yield(image) : image
206
+ end
207
+
244
208
  # A top-level image loader opens path and then yields/returns the image.
245
- #
246
209
  def self.with_image(path)
247
210
  raise ArgumentError, "file does not exist: #{path}" unless File.file?(path)
248
211
  image = guard { with_image_impl(JFile.new(path)) }
249
212
  image && block_given? ? yield(image) : image
250
213
  end
251
214
 
252
- ##
253
- #
254
215
  # A top-level image loader reads bytes and then yields/returns the image.
255
- #
256
216
  def self.with_bytes(bytes)
257
- bytes = bytes.to_java_bytes if String === bytes
217
+ bytes = bytes.to_java_bytes if bytes.is_a? String
258
218
  image = guard { with_bytes_impl(bytes) }
259
219
  block_given? ? yield(image) : image
260
220
  end
261
221
 
262
222
  class << self
263
- alias_method :with_image_from_memory, :with_bytes
223
+ alias with_image_from_memory with_bytes
264
224
  end
265
225
 
266
- ##
267
- #
268
226
  # *_impl providers only need provide the implementation if it can
269
227
  # support it. Otherwise, this method will detect that the method is
270
228
  # missing.
271
- #
272
229
  def self.guard(&block)
273
- begin
274
- return block.call
275
- rescue NoMethodError => e
276
- "Unimplemented Feature: #{e}"
277
- end
230
+ return block.call
231
+ rescue NoMethodError => e
232
+ "Unimplemented Feature: #{e}"
278
233
  end
234
+
279
235
  def guard(&block)
280
236
  ImageVoodoo.guard(&block)
281
237
  end
282
238
 
283
- ##
284
- #
285
239
  # Returns the height of the image, in pixels.
286
- #
287
240
  def height
288
241
  @src.height
289
242
  end
290
243
 
291
- ##
292
- #
293
244
  # Returns the width of the image, in pixels.
294
- #
295
245
  def width
296
246
  @src.width
297
247
  end
298
248
 
299
- ##
300
- #
301
249
  # Returns the underlying Java class associated with this object. Note:
302
250
  # Depending on whether you are using AWT or GAE/J you will get a totally
303
251
  # different Java class. So caveat emptor!
304
- #
305
252
  def to_java
306
253
  @src
307
254
  end
308
255
 
309
- ##
310
- #
311
256
  # Returns detected image format from binary representation of input data
312
257
  # as upper case string. Eg. JPEG, BMP, PNG. For GWT image representation
313
258
  # compatibility method name is :format. It also accepts block and returns
314
259
  # format as first block argument. When format not detected or not set it
315
260
  # returns nil
316
- #
317
261
  def format
318
262
  @format && block_given? ? yield(@format) : @format
319
263
  end
264
+
265
+ def to_radians(degrees)
266
+ degrees * Math::PI / 180
267
+ end
320
268
  end