image_voodoo 0.8.8 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/image_voodoo.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Before we load image_voodoo we can specify whether we want it to load full
2
4
  # AWT ala http://www.oracle.com/technetwork/articles/javase/headless-136834.html
3
5
  # Most users are using image_voodoo as a library for manipulation and do not
@@ -5,10 +7,7 @@
5
7
  unless defined? ImageVoodoo::NEEDS_HEAD
6
8
  java.lang.System.set_property 'java.awt.headless', 'true'
7
9
  end
8
-
9
10
 
10
- ##
11
- #
12
11
  # = ImageVoodoo
13
12
  # == Description
14
13
  #
@@ -33,24 +32,22 @@ end
33
32
  #
34
33
  # img = ImageVoodoo.with_image(ARGV[0])
35
34
  # negative_img = img.negative
36
- #
37
35
  class ImageVoodoo
38
- attr_accessor :quality
36
+ attr_writer :quality # used by quality(value)
39
37
 
40
38
  include Java
41
39
 
42
40
  JFile = java.io.File
43
41
 
44
- ##
45
42
  # FIXME: This has an issue when used in test/unit where the classcastexception
46
43
  # is throwing the stack trace to output. This does not happen when used
47
44
  # directly. Not sure....
48
45
  # gae and awt define the technology-specific methods and more importantly
49
46
  # all the *_impl methods which you will see referenced in this file.
50
47
  begin
51
- require 'image_voodoo/gae'
48
+ require 'image_voodoo/gae'
52
49
  rescue
53
- require 'image_voodoo/awt'
50
+ require 'image_voodoo/awt'
54
51
  end
55
52
 
56
53
  def initialize(io, src, format=nil)
@@ -58,263 +55,217 @@ class ImageVoodoo
58
55
  @quality = nil # nil means no specific quality ever specified
59
56
  end
60
57
 
61
- ##
62
- #
58
+ # Gets RGB value within the source image at [x, y]. If using AWT backend
59
+ # then consider using color_at as this is a Java signed int value of an
60
+ # unsigned value.
61
+ def pixel(x, y)
62
+ @src.getRGB(x, y)
63
+ end
64
+
63
65
  # Adjusts the brightness of each pixel in image by the following formula:
64
66
  # new_pixel = pixel * scale + offset
65
- #
66
67
  def adjust_brightness(scale, offset)
67
68
  image = guard { adjust_brightness_impl(scale, offset) }
68
69
  block_given? ? yield(image) : image
69
70
  end
70
71
 
71
- ##
72
- #
73
72
  # Converts rgb hex color value to an alpha value an yields/returns the new
74
73
  # image.
75
- #
76
74
  def alpha(rgb)
77
75
  target = guard { alpha_impl(rgb) }
78
76
  block_given? ? yield(target) : target
79
77
  end
80
78
 
81
- ##
82
- #
83
79
  # Get current image bytes as a String using provided format. Format parameter
84
80
  # is the informal name of an image type - for instance,
85
81
  # "bmp" or "jpg". If the backend is AWT the types available are listed in
86
82
  # javax.imageio.ImageIO.getWriterFormatNames()
87
- #
88
83
  def bytes(format)
89
84
  java_bytes = guard { bytes_impl(format) }
90
85
  String.from_java_bytes java_bytes
91
86
  end
92
87
 
93
- ##
94
88
  # If current image was taken by a phone it might save the orientation
95
89
  # in format it was physically taken and added IFD0 Orientation information
96
90
  # instead of rotating it. This method will perform that rotation based
97
91
  # on Orientation metadata.
98
- #
99
92
  def correct_orientation
100
93
  target = guard { correct_orientation_impl }
101
94
  block_given? ? yield(target) : target
102
95
  end
103
96
 
104
- ##
105
- #
106
97
  # Creates a square thumbnail of the image cropping the longest edge to
107
98
  # match the shortest edge, resizes to size, and yields/returns the new image.
108
- #
109
99
  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
-
100
+ l, t, r, b = calculate_thumbnail_dimensions
114
101
  target = with_crop(l, t, r, b).thumbnail(size)
115
102
  block_given? ? yield(target) : target
116
103
  end
117
104
 
118
- ##
119
- #
105
+ private def calculate_thumbnail_dimensions
106
+ half = (width - height).abs / 2
107
+ if width > height
108
+ [half, 0, half + height, height]
109
+ else
110
+ [0, half, width, half + width]
111
+ end
112
+ end
113
+
120
114
  # Flips the image horizontally and yields/returns the new image.
121
- #
122
115
  def flip_horizontally
123
116
  target = guard { flip_horizontally_impl }
124
117
  block_given? ? yield(target) : target
125
118
  end
126
119
 
127
- ##
128
- #
129
120
  # Flips the image vertically and yields/returns the new image.
130
- #
131
121
  def flip_vertically
132
122
  target = guard { flip_vertically_impl }
133
123
  block_given? ? yield(target) : target
134
124
  end
135
125
 
136
- ##
137
- #
138
126
  # Creates a grayscale version of image and yields/returns the new image.
139
- #
140
127
  def greyscale
141
128
  target = guard { greyscale_impl }
142
129
  block_given? ? yield(target) : target
143
130
  end
144
- alias_method :grayscale, :greyscale
131
+ alias grayscale greyscale
145
132
 
146
- ##
147
- #
148
133
  # Extracts metadata from an image.
149
- #
150
134
  def metadata
151
135
  guard { metadata_impl }
152
136
  end
153
137
 
154
- ##
155
- #
156
138
  # Creates a negative and yields/returns the new image.
157
- #
158
139
  def negative
159
140
  target = guard { negative_impl }
160
141
  block_given? ? yield(target) : target
161
142
  end
162
143
 
163
- ##
164
- #
165
144
  # Set quality you want resulting image to be once you save or extract
166
145
  # bytes for the image. Note: This will only work for lossy image
167
146
  # formats like PNG of JPEG. For others it will be ignored.
168
147
  def quality(amount)
169
148
  if amount < 0.0 || amount > 1.0
170
- raise ArgumentError.new "Quality must be between 0.0 and 1.0"
149
+ raise ArgumentError, 'Quality must be between 0.0 and 1.0'
171
150
  end
172
151
 
173
- target = self.dup
152
+ target = dup
174
153
  target.quality = amount
175
154
  block_given? ? yield(target) : target
176
155
  end
177
156
 
178
- ##
179
- #
180
157
  # Resizes the image to width and height and yields/returns the new image.
181
- #
182
158
  def resize(width, height)
183
159
  target = guard { resize_impl(width, height) }
184
160
  block_given? ? yield(target) : target
185
- rescue NativeException => ne
186
- raise ArgumentError, ne.message
161
+ rescue java.lang.Exception => e # figure out why this is here at all?
162
+ raise ArgumentError, e.message
187
163
  end
188
164
 
189
- ##
190
- #
191
165
  # Rotates the image by angle (specified in degrees).
192
- #
193
166
  def rotate(angle)
194
- target = guard { rotate_impl(angle) }
167
+ target = guard { rotate_impl(to_radians(angle)) }
195
168
  block_given? ? yield(target) : target
196
169
  end
197
170
 
198
- ##
199
- #
200
171
  # Saves the image out to path. Changing the file extension will convert
201
172
  # the file type to the appropriate format.
202
- #
203
173
  def save(file)
204
174
  format = File.extname(file)
205
- return false if format == ""
175
+ return false if format == ''
176
+
206
177
  format = format[1..-1].downcase
207
178
  guard { save_impl(format, JFile.new(file)) }
208
179
  true
209
180
  end
210
181
 
211
- ##
212
- #
213
182
  # Resize (scale) the current image by the provided ratio and yield/return
214
183
  # the new image.
215
- #
216
184
  def scale(ratio)
217
185
  new_width, new_height = (width * ratio).to_i, (height * ratio).to_i
218
186
  target = resize(new_width, new_height)
219
187
  block_given? ? yield(target) : target
220
188
  end
221
189
 
222
- ##
223
- #
224
190
  # Creates a proportional thumbnail of the image scaled so its longest
225
191
  # edge is resized to size and yields/returns the new image.
226
- #
227
192
  def thumbnail(size)
228
193
  target = scale(size.to_f / (width > height ? width : height))
229
194
  block_given? ? yield(target) : target
230
195
  end
231
196
 
232
- ##
233
- #
234
197
  # Crops an image to left, top, right, and bottom and then yields/returns the
235
198
  # new image.
236
- #
237
199
  def with_crop(left, top, right, bottom)
238
200
  image = guard { with_crop_impl(left, top, right, bottom) }
239
201
  block_given? ? yield(image) : image
240
202
  end
241
203
 
242
- ##
243
- #
204
+ # Creates a new (empty) image with a file name specified.
205
+ def self.new_image(width, height, file_name)
206
+ image = guard { new_image_impl(width, height, file_name) }
207
+ block_given? ? yield(image) : image
208
+ end
209
+
244
210
  # A top-level image loader opens path and then yields/returns the image.
245
- #
246
211
  def self.with_image(path)
247
212
  raise ArgumentError, "file does not exist: #{path}" unless File.file?(path)
213
+
248
214
  image = guard { with_image_impl(JFile.new(path)) }
249
215
  image && block_given? ? yield(image) : image
250
216
  end
251
217
 
252
- ##
253
- #
254
218
  # A top-level image loader reads bytes and then yields/returns the image.
255
- #
256
219
  def self.with_bytes(bytes)
257
- bytes = bytes.to_java_bytes if String === bytes
220
+ bytes = bytes.to_java_bytes if bytes.is_a? String
258
221
  image = guard { with_bytes_impl(bytes) }
259
222
  block_given? ? yield(image) : image
260
223
  end
261
224
 
262
225
  class << self
263
- alias_method :with_image_from_memory, :with_bytes
226
+ alias with_image_from_memory with_bytes
264
227
  end
265
228
 
266
- ##
267
- #
268
229
  # *_impl providers only need provide the implementation if it can
269
230
  # support it. Otherwise, this method will detect that the method is
270
231
  # missing.
271
- #
272
232
  def self.guard(&block)
273
- begin
274
- return block.call
275
- rescue NoMethodError => e
276
- "Unimplemented Feature: #{e}"
277
- end
233
+ block.call
234
+ rescue NoMethodError => e
235
+ "Unimplemented Feature: #{e}"
278
236
  end
237
+
279
238
  def guard(&block)
280
239
  ImageVoodoo.guard(&block)
281
240
  end
282
241
 
283
- ##
284
- #
285
242
  # Returns the height of the image, in pixels.
286
- #
287
243
  def height
288
244
  @src.height
289
245
  end
290
246
 
291
- ##
292
- #
293
247
  # Returns the width of the image, in pixels.
294
- #
295
248
  def width
296
249
  @src.width
297
250
  end
298
251
 
299
- ##
300
- #
301
252
  # Returns the underlying Java class associated with this object. Note:
302
253
  # Depending on whether you are using AWT or GAE/J you will get a totally
303
254
  # different Java class. So caveat emptor!
304
- #
305
255
  def to_java
306
256
  @src
307
257
  end
308
258
 
309
- ##
310
- #
311
259
  # Returns detected image format from binary representation of input data
312
260
  # as upper case string. Eg. JPEG, BMP, PNG. For GWT image representation
313
261
  # compatibility method name is :format. It also accepts block and returns
314
262
  # format as first block argument. When format not detected or not set it
315
263
  # returns nil
316
- #
317
264
  def format
318
265
  @format && block_given? ? yield(@format) : @format
319
266
  end
267
+
268
+ def to_radians(degrees)
269
+ degrees * Math::PI / 180
270
+ end
320
271
  end
data/samples/bench.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/local/bin/ruby -w
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'benchmark'
4
5
  require 'rbconfig'
@@ -6,59 +7,52 @@ require 'rubygems'
6
7
  require 'image_science'
7
8
 
8
9
  max = (ARGV.shift || 100).to_i
9
- ext = ARGV.shift || "png"
10
-
10
+ ext = ARGV.shift || 'png'
11
11
  file = "blah_big.#{ext}"
12
12
 
13
- if Config::CONFIG['host_os'] =~ /darwin/ then
14
- # how fucking cool is this???
15
- puts "taking screenshot for thumbnailing benchmarks"
16
- system "screencapture -SC #{file}"
17
- else
18
- abort "You need to plonk down #{file} or buy a mac"
19
- end unless test ?f, "#{file}"
13
+ unless File.exist?(file)
14
+ if RbConfig::CONFIG['host_os'] =~ /darwin/i
15
+ puts 'taking screenshot for thumbnailing benchmarks'
16
+ system "screencapture -SC #{file}"
17
+ elsif RbConfig::CONFIG['host_os'] =~ /linux/i
18
+ puts 'taking screenshot for thumbnailing benchmarks'
19
+ system "gnome-screenshot -f #{file}"
20
+ else
21
+ abort "You need to save an image to #{file} since we cannot generate one"
22
+ end
23
+ end
20
24
 
21
- ImageScience.with_image(file.sub(/#{ext}$/, 'png')) do |img|
22
- img.save(file)
23
- end if ext != "png"
25
+ if ext != 'png'
26
+ ImageScience.with_image(file.sub(/#{ext}$/, 'png')) { |img| img.save(file) }
27
+ end
24
28
 
25
29
  puts "# of iterations = #{max}"
26
- Benchmark::bm(20) do |x|
27
- x.report("null_time") {
28
- for i in 0..max do
29
- # do nothing
30
- end
31
- }
30
+ Benchmark.bm(20) do |x|
31
+ x.report('null_time') { max.times {} }
32
32
 
33
- x.report("cropped") {
34
- for i in 0..max do
33
+ x.report('cropped') do
34
+ max.times do
35
35
  ImageScience.with_image(file) do |img|
36
- img.cropped_thumbnail(100) do |thumb|
37
- thumb.save("blah_cropped.#{ext}")
38
- end
36
+ img.cropped_thumbnail(100) { |thumb| thumb.save("blah_cropped.#{ext}") }
39
37
  end
40
38
  end
41
- }
39
+ end
42
40
 
43
- x.report("proportional") {
44
- for i in 0..max do
41
+ x.report('proportional') do
42
+ max.times do
45
43
  ImageScience.with_image(file) do |img|
46
- img.thumbnail(100) do |thumb|
47
- thumb.save("blah_thumb.#{ext}")
48
- end
44
+ img.thumbnail(100) { |thumb| thumb.save("blah_thumb.#{ext}") }
49
45
  end
50
46
  end
51
- }
47
+ end
52
48
 
53
- x.report("resize") {
54
- for i in 0..max do
49
+ x.report('resize') do
50
+ max.times do
55
51
  ImageScience.with_image(file) do |img|
56
- img.resize(200, 200) do |resize|
57
- resize.save("blah_resize.#{ext}")
58
- end
52
+ img.resize(200, 200) { |resize| resize.save("blah_resize.#{ext}") }
59
53
  end
60
54
  end
61
- }
55
+ end
62
56
  end
63
57
 
64
58
  # File.unlink(*Dir["blah*#{ext}"])
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'image_voodoo'
2
4
 
3
5
  # reads in the file specified by ARGV[0], transforming to greyscale and
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'image_science'
2
4
 
3
5
  # reads in the file specified by ARGV[0], transforms it into a 32-pixel thumbnail,
data/samples/file_view.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'image_voodoo/needs_head'
1
4
  require 'image_voodoo'
2
5
 
3
- ImageVoodoo.with_image(ARGV[0]) { |img| img.preview }
6
+ ImageVoodoo.with_image(ARGV[0], &:preview)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'image_voodoo'
2
4
 
3
5
  # reads in the image at ARGV[0], transforms it into a 32-pixel thumbnail in-memory,
data/samples/lossy.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'image_voodoo'
2
4
 
3
5
  ImageVoodoo.with_image(ARGV[0]) do |img|
@@ -1,9 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test/unit/testcase'
2
- require 'test/unit' if $0 == __FILE__
4
+ require 'test/unit' if $PROGRAM_NAME == __FILE__
3
5
  require 'image_science'
4
6
 
5
7
  class TestImageScience < Test::Unit::TestCase
6
- def deny x; assert ! x; end
8
+ def deny(x)
9
+ assert !x
10
+ end
11
+
12
+ def similar_image?(w, h, img)
13
+ assert_kind_of ImageScience, img
14
+ assert_equal h, img.height
15
+ assert_equal w, img.width
16
+ end
7
17
 
8
18
  def setup
9
19
  @path = 'test/pix.png'
@@ -17,118 +27,68 @@ class TestImageScience < Test::Unit::TestCase
17
27
 
18
28
  def test_class_with_image
19
29
  ImageScience.with_image @path do |img|
20
- assert_kind_of ImageScience, img
21
- assert_equal @h, img.height
22
- assert_equal @w, img.width
30
+ similar_image? @w, @h, img
23
31
  assert img.save(@tmppath)
24
32
  end
25
33
 
26
- assert File.exists?(@tmppath)
27
-
28
- ImageScience.with_image @tmppath do |img|
29
- assert_kind_of ImageScience, img
30
- assert_equal @h, img.height
31
- assert_equal @w, img.width
32
- end
34
+ ImageScience.with_image(@tmppath) { |img| similar_image? @w, @h, img }
33
35
  end
34
36
 
35
37
  def test_class_with_image_missing
36
38
  assert_raises ArgumentError do
37
- ImageScience.with_image @path + "nope" do |img|
38
- flunk
39
- end
39
+ ImageScience.with_image("#{@path}nope") { flunk }
40
40
  end
41
41
  end
42
42
 
43
43
  def test_class_with_image_missing_with_img_extension
44
44
  assert_raises ArgumentError do
45
- ImageScience.with_image("nope#{@path}") do |img|
46
- flunk
47
- end
45
+ ImageScience.with_image("nope#{@path}") { flunk }
48
46
  end
49
47
  end
50
48
 
51
49
  def test_class_with_image_return_nil_on_bogus_image
52
- File.open(@tmppath, "w") {|f| f << "bogus image file"}
53
- assert_nil ImageScience.with_image(@tmppath) do |img|
54
- flunk
55
- end
50
+ File.open(@tmppath, 'w') { |f| f << 'bogus image file' }
51
+ assert_nil ImageScience.with_image(@tmppath) { flunk }
56
52
  end
57
53
 
58
54
  def test_resize
59
55
  ImageScience.with_image @path do |img|
60
- img.resize(25, 25) do |thumb|
61
- assert thumb.save(@tmppath)
62
- end
56
+ img.resize(25, 25) { |thumb| assert thumb.save(@tmppath) }
63
57
  end
64
58
 
65
- assert File.exists?(@tmppath)
66
-
67
- ImageScience.with_image @tmppath do |img|
68
- assert_kind_of ImageScience, img
69
- assert_equal 25, img.height
70
- assert_equal 25, img.width
71
- end
59
+ ImageScience.with_image(@tmppath) { |img| similar_image? 25, 25, img }
72
60
  end
73
61
 
74
62
  def test_resize_floats
75
63
  ImageScience.with_image @path do |img|
76
- img.resize(25.2, 25.7) do |thumb|
77
- assert thumb.save(@tmppath)
78
- end
64
+ img.resize(25.2, 25.7) { |thumb| assert thumb.save(@tmppath) }
79
65
  end
80
66
 
81
- assert File.exists?(@tmppath)
82
-
83
- ImageScience.with_image @tmppath do |img|
84
- assert_kind_of ImageScience, img
85
- assert_equal 25, img.height
86
- assert_equal 25, img.width
87
- end
67
+ ImageScience.with_image(@tmppath) { |img| similar_image? 25, 25, img }
88
68
  end
89
69
 
90
70
  def test_resize_zero
91
- assert_raises ArgumentError do
92
- ImageScience.with_image @path do |img|
93
- img.resize(0, 25) do |thumb|
94
- assert thumb.save(@tmppath)
71
+ [[0, 25], [25, 0], [0, 0]].each do |w, h|
72
+ assert_raises ArgumentError do
73
+ ImageScience.with_image @path do |img|
74
+ img.resize(w, h) { |thumb| assert thumb.save(@tmppath) }
95
75
  end
96
76
  end
97
- end
98
77
 
99
- deny File.exists?(@tmppath)
100
-
101
- assert_raises ArgumentError do
102
- ImageScience.with_image @path do |img|
103
- img.resize(25, 0) do |thumb|
104
- assert thumb.save(@tmppath)
105
- end
106
- end
78
+ deny File.exist?(@tmppath)
107
79
  end
108
-
109
- deny File.exists?(@tmppath)
110
80
  end
111
81
 
112
82
  def test_resize_negative
113
- assert_raises ArgumentError do
114
- ImageScience.with_image @path do |img|
115
- img.resize(-25, 25) do |thumb|
116
- assert thumb.save(@tmppath)
83
+ [[-25, 25], [25, -25], [-25, -25]].each do |w, h|
84
+ assert_raises ArgumentError do
85
+ ImageScience.with_image @path do |img|
86
+ img.resize(w, h) { |thumb| assert thumb.save(@tmppath) }
117
87
  end
118
88
  end
119
- end
120
89
 
121
- deny File.exists?(@tmppath)
122
-
123
- assert_raises ArgumentError do
124
- ImageScience.with_image @path do |img|
125
- img.resize(25, -25) do |thumb|
126
- assert thumb.save(@tmppath)
127
- end
128
- end
90
+ deny File.exist?(@tmppath)
129
91
  end
130
-
131
- deny File.exists?(@tmppath)
132
92
  end
133
93
 
134
94
  def test_image_format_retrieval
@@ -147,7 +107,7 @@ class TestImageScience < Test::Unit::TestCase
147
107
  end
148
108
 
149
109
  def test_image_format_retrieval_fail_when_invalid_bytes
150
- image = ImageScience.with_bytes("some invalid image bytes")
110
+ image = ImageScience.with_bytes('some invalid image bytes')
151
111
  assert_equal nil, image.format
152
112
  end
153
113
 
@@ -166,6 +126,6 @@ class TestImageScience < Test::Unit::TestCase
166
126
  assert new_img.save(@tmppath)
167
127
  end
168
128
 
169
- assert File.exists?(@tmppath)
129
+ assert File.exist?(@tmppath)
170
130
  end
171
131
  end