image_voodoo 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,6 +5,8 @@ README.txt
5
5
  LICENSE.txt
6
6
  lib/image_science.rb
7
7
  lib/image_voodoo
8
+ lib/image_voodoo/awt.rb
9
+ lib/image_voodoo/gae.rb
8
10
  lib/image_voodoo/version.rb
9
11
  lib/image_voodoo.rb
10
12
  samples/bench.rb
@@ -27,62 +27,29 @@
27
27
  class ImageVoodoo
28
28
  include Java
29
29
 
30
- import java.awt.RenderingHints
31
- import java.awt.color.ColorSpace
32
- import java.awt.geom.AffineTransform
33
- import java.awt.image.BufferedImage
34
- import java.awt.image.ByteLookupTable
35
- import java.awt.image.ColorConvertOp
36
- import java.awt.image.LookupOp
37
- import java.awt.image.RescaleOp
38
30
  JFile = java.io.File
39
- import java.io.ByteArrayInputStream
40
- import java.io.ByteArrayOutputStream
41
- import javax.imageio.ImageIO
42
- import javax.swing.JFrame
43
31
 
44
- NEGATIVE_OP = LookupOp.new(ByteLookupTable.new(0, (0...254).to_a.reverse.to_java(:byte)), nil)
45
- GREY_OP = ColorConvertOp.new(ColorSpace.getInstance(ColorSpace::CS_GRAY), nil)
46
- ARGB = BufferedImage::TYPE_INT_ARGB
47
- RGB = BufferedImage::TYPE_INT_RGB
48
- SCALE_SMOOTH = java.awt.Image::SCALE_SMOOTH
32
+ # FIXME: This has an issue when used in test/unit where the classcastexception
33
+ # is throwing the stack trace to output. This does not happen when used
34
+ # directly. Not sure....
35
+ # gae and awt define the technology-specific methods and more importantly
36
+ # all the *_impl methods which you will see referenced in this file.
37
+ begin
38
+ require 'image_voodoo/gae'
39
+ rescue
40
+ require 'image_voodoo/awt'
41
+ end
49
42
 
50
43
  def initialize(src)
51
44
  @src = src
52
45
  end
53
46
 
54
- #
55
- # Add a border to the image and yield/return a new image. The following
56
- # options are supported:
57
- # - width: How thick is the border (default: 3)
58
- # - color: Which color is the border (in rrggbb hex value)
59
- # - style: etched, raised, plain (default: plain)
60
- #
61
- def add_border(options = {})
62
- border_width = options[:width].to_i || 2
63
- color = hex_to_color(options[:color]) || hex_to_color("000000")
64
- style = options[:style]
65
- style = nil if style.to_sym == :plain
66
- new_width, new_height = width + 2*border_width, height + 2*border_width
67
- target = paint(BufferedImage.new(new_width, new_height, color_type)) do |g|
68
- g.color = color
69
- if style
70
- raised = style.to_sym == :raised ? true : false
71
- g.fill3DRect(0, 0, new_width, new_height, raised)
72
- else
73
- g.fill_rect(0, 0, new_width, new_height)
74
- end
75
- g.draw_image(@src, nil, border_width, border_width)
76
- end
77
- block_given? ? yield(target) : target
78
- end
79
-
80
47
  #
81
48
  # Adjusts the brightness of each pixel in image by the following formula:
82
49
  # new_pixel = pixel * scale + offset
83
50
  #
84
51
  def adjust_brightness(scale, offset)
85
- image = internal_transform(RescaleOp.new(scale, offset, nil))
52
+ image = guard { adjust_brightness_impl(scale, offset) }
86
53
  block_given? ? yield(image) : image
87
54
  end
88
55
 
@@ -91,16 +58,7 @@ class ImageVoodoo
91
58
  # image.
92
59
  #
93
60
  def alpha(rgb)
94
- color = hex_to_color(rgb)
95
- target = paint(BufferedImage.new(width, height, ARGB)) do |g|
96
- g.set_composite(java.awt.AlphaComposite::Src)
97
- g.draw_image(@src, nil, 0, 0)
98
- 0.upto(height-1) do |i|
99
- 0.upto(width-1) do |j|
100
- target.setRGB(j, i, 0x8F1C1C) if target.getRGB(j, i) == color.getRGB
101
- end
102
- end
103
- end
61
+ target = guard { alpha_impl(rgb) }
104
62
  block_given? ? yield(target) : target
105
63
  end
106
64
 
@@ -108,9 +66,8 @@ class ImageVoodoo
108
66
  # Write current image out as a stream of bytes using provided format.
109
67
  #
110
68
  def bytes(format)
111
- out = ByteArrayOutputStream.new
112
- ImageIO.write(@src, format, out)
113
- String.from_java_bytes(out.to_byte_array)
69
+ java_bytes = guard { bytes_impl(format) }
70
+ String.from_java_bytes java_bytes
114
71
  end
115
72
 
116
73
  #
@@ -130,9 +87,7 @@ class ImageVoodoo
130
87
  # Flips the image horizontally and yields/returns the new image.
131
88
  #
132
89
  def flip_horizontally
133
- target = paint do |g|
134
- g.draw_image @src, 0, 0, width, height, width, 0, 0, height, nil
135
- end
90
+ target = guard { flip_horizontally_impl }
136
91
  block_given? ? yield(target) : target
137
92
  end
138
93
 
@@ -140,9 +95,7 @@ class ImageVoodoo
140
95
  # Flips the image vertically and yields/returns the new image.
141
96
  #
142
97
  def flip_vertically
143
- target = paint do |g|
144
- g.draw_image @src, 0, 0, width, height, 0, height, width, 0, nil
145
- end
98
+ target = guard { flip_vertically_impl }
146
99
  block_given? ? yield(target) : target
147
100
  end
148
101
 
@@ -150,7 +103,7 @@ class ImageVoodoo
150
103
  # Creates a grayscale version of image and yields/returns the new image.
151
104
  #
152
105
  def greyscale
153
- target = internal_transform(GREY_OP)
106
+ target = guard { greyscale_impl }
154
107
  block_given? ? yield(target) : target
155
108
  end
156
109
  alias_method :grayscale, :greyscale
@@ -159,19 +112,15 @@ class ImageVoodoo
159
112
  # Creates a negative and yields/returns the new image.
160
113
  #
161
114
  def negative
162
- target = internal_transform(NEGATIVE_OP)
115
+ target = guard { negative_impl }
163
116
  block_given? ? yield(target) : target
164
117
  end
165
118
 
166
119
  #
167
- # Resizes the image to width and height using bicubic interpolation and
168
- # yields/returns the new image.
120
+ # Resizes the image to width and height and yields/returns the new image.
169
121
  #
170
122
  def resize(width, height)
171
- target = paint(BufferedImage.new(width, height, color_type)) do |g|
172
- scaled_image = @src.get_scaled_instance width, height, SCALE_SMOOTH
173
- g.draw_image scaled_image, 0, 0, nil
174
- end
123
+ target = guard { resize_impl(width, height) }
175
124
  block_given? ? yield(target) : target
176
125
  rescue NativeException => ne
177
126
  raise ArgumentError, ne.message
@@ -185,7 +134,7 @@ class ImageVoodoo
185
134
  format = File.extname(file)
186
135
  return false if format == ""
187
136
  format = format[1..-1].downcase
188
- ImageIO.write(@src, format, JFile.new(file))
137
+ guard { save_impl(format, JFile.new(file)) }
189
138
  true
190
139
  end
191
140
 
@@ -213,79 +162,16 @@ class ImageVoodoo
213
162
  # new image.
214
163
  #
215
164
  def with_crop(left, top, right, bottom)
216
- image = ImageVoodoo.new @src.get_subimage(left, top, right-left, bottom-top)
165
+ image = guard { with_crop_impl(left, top, right, bottom) }
217
166
  block_given? ? yield(image) : image
218
167
  end
219
168
 
220
- #
221
- # A simple swing wrapper around an image voodoo object.
222
- #
223
- class JImagePanel < javax.swing.JPanel
224
- def initialize(image, x=0, y=0)
225
- super()
226
- @image, @x, @y = image, x, y
227
- end
228
-
229
- def image=(image)
230
- @image = image
231
- invalidate
232
- end
233
-
234
- def getPreferredSize
235
- java.awt.Dimension.new(@image.width, @image.height)
236
- end
237
-
238
- def paintComponent(graphics)
239
- graphics.draw_image(@image.to_java, @x, @y, nil)
240
- end
241
- end
242
-
243
- # Internal class for closing preview window
244
- class WindowClosed
245
- def initialize(block = nil)
246
- @block = block || proc { java.lang.System.exit(0) }
247
- end
248
- def method_missing(meth,*args); end
249
- def windowClosing(event); @block.call; end
250
- end
251
-
252
- #
253
- # Creates a viewable frame displaying current image within it.
254
- #
255
- def preview(&block)
256
- frame = JFrame.new("Preview")
257
- frame.add_window_listener WindowClosed.new(block)
258
- frame.set_bounds 0, 0, width + 20, height + 40
259
- frame.add JImagePanel.new(self, 10, 10)
260
- frame.visible = true
261
- end
262
-
263
- #
264
- # TODO: Figure out how to determine whether source has alpha or not
265
- # Experimental: Read an image from the url source and yield/return that
266
- # image.
267
- #
268
- def self.from_url(source)
269
- url = java.net.URL.new(source)
270
- image = java.awt.Toolkit.default_toolkit.create_image(url)
271
- tracker = java.awt.MediaTracker.new(java.awt.Label.new(""))
272
- tracker.addImage(image, 0);
273
- tracker.waitForID(0)
274
- target = paint(BufferedImage.new(image.width, image.height, RGB)) do |g|
275
- g.draw_image image, 0, 0, nil
276
- end
277
- block_given? ? yield(target) : target
278
- rescue java.io.IOException, java.net.MalformedURLException
279
- raise ArgumentError.new "Trouble retrieving image: #{$!.message}"
280
- end
281
-
282
169
  #
283
170
  # A top-level image loader opens path and then yields/returns the image.
284
171
  #
285
172
  def self.with_image(file)
286
173
  raise ArgumentError, "file does not exist" unless File.file?(file)
287
- buffered_image = ImageIO.read(JFile.new(file))
288
- image = ImageVoodoo.new(buffered_image) if buffered_image
174
+ image = guard { with_image_impl(JFile.new(file)) }
289
175
  image && block_given? ? yield(image) : image
290
176
  end
291
177
 
@@ -294,10 +180,26 @@ class ImageVoodoo
294
180
  #
295
181
  def self.with_bytes(bytes)
296
182
  bytes = bytes.to_java_bytes if String === bytes
297
- image = ImageVoodoo.new ImageIO.read(ByteArrayInputStream.new(bytes))
183
+ image = guard { with_bytes_impl(bytes) }
298
184
  block_given? ? yield(image) : image
299
185
  end
300
186
 
187
+ #
188
+ # *_impl providers only need provide the implementation if it can
189
+ # support it. Otherwise, this method will detect that the method is
190
+ # missing.
191
+ #
192
+ def self.guard(&block)
193
+ begin
194
+ return block.call
195
+ rescue NoMethodError => e
196
+ "Unimplemented Feature: #{e}"
197
+ end
198
+ end
199
+ def guard(&block)
200
+ ImageVoodoo.guard(&block)
201
+ end
202
+
301
203
  #
302
204
  # Returns the height of the image, in pixels.
303
205
  #
@@ -313,55 +215,11 @@ class ImageVoodoo
313
215
  end
314
216
 
315
217
  #
316
- # Returns the underlying Java BufferedImage associated with this object.
218
+ # Returns the underlying Java class associated with this object. Note:
219
+ # Depending on whether you are using AWT or GAE/J you will get a totally
220
+ # different Java class. So caveat emptor!
317
221
  #
318
222
  def to_java
319
223
  @src
320
224
  end
321
-
322
- private
323
-
324
- #
325
- # Converts a RGB hex value into a java.awt.Color object or dies trying
326
- # with an ArgumentError.
327
- #
328
- def hex_to_color(rgb)
329
- raise ArgumentError.new "hex rrggbb needed" if rgb !~ /[[:xdigit:]]{6,6}/
330
-
331
- java.awt.Color.new(rgb[0,2].to_i(16), rgb[2,2].to_i(16), rgb[4,2].to_i(16))
332
- end
333
-
334
- #
335
- # Determines the best colorspace for a new image based on whether the
336
- # existing image contains an alpha channel or not.
337
- #
338
- def color_type
339
- @src.color_model.has_alpha ? ARGB : RGB
340
- end
341
-
342
- #
343
- # DRY up drawing setup+teardown
344
- #
345
- def paint(src=dup_src)
346
- yield src.graphics
347
- src.graphics.dispose
348
- ImageVoodoo.new src
349
- end
350
-
351
- #
352
- # Make a duplicate of the underlying Java src image
353
- #
354
- def dup_src
355
- BufferedImage.new width, height, color_type
356
- end
357
-
358
- #
359
- # Do simple AWT operation transformation to target.
360
- #
361
- def internal_transform(operation, target=dup_src)
362
- paint(target) do |g|
363
- g.draw_image(@src, 0, 0, nil)
364
- g.draw_image(operation.filter(target, nil), 0, 0, nil)
365
- end
366
- end
367
225
  end
@@ -0,0 +1,223 @@
1
+ class ImageVoodoo
2
+ java_import java.awt.RenderingHints
3
+ java_import java.awt.color.ColorSpace
4
+ java_import java.awt.geom.AffineTransform
5
+ java_import java.awt.image.BufferedImage
6
+ java_import java.awt.image.ByteLookupTable
7
+ java_import java.awt.image.ColorConvertOp
8
+ java_import java.awt.image.LookupOp
9
+ java_import java.awt.image.RescaleOp
10
+ java_import java.io.ByteArrayInputStream
11
+ java_import java.io.ByteArrayOutputStream
12
+ java_import javax.imageio.ImageIO
13
+ java_import javax.swing.JFrame
14
+
15
+ NEGATIVE_OP = LookupOp.new(ByteLookupTable.new(0, (0...254).to_a.reverse.to_java(:byte)), nil)
16
+ GREY_OP = ColorConvertOp.new(ColorSpace.getInstance(ColorSpace::CS_GRAY), nil)
17
+ ARGB = BufferedImage::TYPE_INT_ARGB
18
+ RGB = BufferedImage::TYPE_INT_RGB
19
+ SCALE_SMOOTH = java.awt.Image::SCALE_SMOOTH
20
+
21
+ #
22
+ # AWT-only (experimental)
23
+ # Add a border to the image and yield/return a new image. The following
24
+ # options are supported:
25
+ # - width: How thick is the border (default: 3)
26
+ # - color: Which color is the border (in rrggbb hex value)
27
+ # - style: etched, raised, plain (default: plain)
28
+ #
29
+ def add_border(options = {})
30
+ border_width = options[:width].to_i || 2
31
+ color = hex_to_color(options[:color]) || hex_to_color("000000")
32
+ style = options[:style]
33
+ style = nil if style.to_sym == :plain
34
+ new_width, new_height = width + 2*border_width, height + 2*border_width
35
+ target = paint(BufferedImage.new(new_width, new_height, color_type)) do |g|
36
+ g.color = color
37
+ if style
38
+ raised = style.to_sym == :raised ? true : false
39
+ g.fill3DRect(0, 0, new_width, new_height, raised)
40
+ else
41
+ g.fill_rect(0, 0, new_width, new_height)
42
+ end
43
+ g.draw_image(@src, nil, border_width, border_width)
44
+ end
45
+ block_given? ? yield(target) : target
46
+ end
47
+
48
+ def adjust_brightness_impl(scale, offset)
49
+ transform(RescaleOp.new(scale, offset, nil))
50
+ end
51
+
52
+ # AWT-only
53
+ def alpha_impl(rgb)
54
+ color = hex_to_color(rgb)
55
+ target = paint(BufferedImage.new(width, height, ARGB)) do |g|
56
+ g.set_composite(java.awt.AlphaComposite::Src)
57
+ g.draw_image(@src, nil, 0, 0)
58
+ 0.upto(height-1) do |i|
59
+ 0.upto(width-1) do |j|
60
+ target.setRGB(j, i, 0x8F1C1C) if target.getRGB(j, i) == color.getRGB
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def bytes_impl(format)
67
+ out = ByteArrayOutputStream.new
68
+ ImageIO.write(@src, format, out)
69
+ out.to_byte_array
70
+ end
71
+
72
+ def flip_horizontally_impl
73
+ paint {|g| g.draw_image @src, 0, 0, width, height, width, 0, 0, height, nil}
74
+ end
75
+
76
+ def flip_vertically_impl
77
+ paint {|g| g.draw_image @src, 0, 0, width, height, 0, height, width, 0, nil}
78
+ end
79
+
80
+ def greyscale_impl
81
+ transform(GREY_OP)
82
+ end
83
+
84
+ def negative_impl
85
+ transform(NEGATIVE_OP)
86
+ end
87
+
88
+ def resize_impl(width, height)
89
+ paint(BufferedImage.new(width, height, color_type)) do |g|
90
+ scaled_image = @src.get_scaled_instance width, height, SCALE_SMOOTH
91
+ g.draw_image scaled_image, 0, 0, nil
92
+ end
93
+ end
94
+
95
+ #
96
+ # Save using the format string (jpg, gif, etc..) to the open Java File
97
+ # instance passed in.
98
+ #
99
+ def save_impl(format, file)
100
+ ImageIO.write(@src, format, file)
101
+ end
102
+
103
+ def with_crop_impl(left, top, right, bottom)
104
+ ImageVoodoo.new @src.get_subimage(left, top, right-left, bottom-top)
105
+ end
106
+
107
+ #
108
+ # A simple swing wrapper around an image voodoo object.
109
+ #
110
+ class JImagePanel < javax.swing.JPanel
111
+ def initialize(image, x=0, y=0)
112
+ super()
113
+ @image, @x, @y = image, x, y
114
+ end
115
+
116
+ def image=(image)
117
+ @image = image
118
+ invalidate
119
+ end
120
+
121
+ def getPreferredSize
122
+ java.awt.Dimension.new(@image.width, @image.height)
123
+ end
124
+
125
+ def paintComponent(graphics)
126
+ graphics.draw_image(@image.to_java, @x, @y, nil)
127
+ end
128
+ end
129
+
130
+ # Internal class for closing preview window
131
+ class WindowClosed
132
+ def initialize(block = nil)
133
+ @block = block || proc { java.lang.System.exit(0) }
134
+ end
135
+ def method_missing(meth,*args); end
136
+ def windowClosing(event); @block.call; end
137
+ end
138
+
139
+ #
140
+ # Creates a viewable frame displaying current image within it.
141
+ #
142
+ def preview(&block)
143
+ frame = JFrame.new("Preview")
144
+ frame.add_window_listener WindowClosed.new(block)
145
+ frame.set_bounds 0, 0, width + 20, height + 40
146
+ frame.add JImagePanel.new(self, 10, 10)
147
+ frame.visible = true
148
+ end
149
+
150
+ #
151
+ # TODO: Figure out how to determine whether source has alpha or not
152
+ # Experimental: Read an image from the url source and yield/return that
153
+ # image.
154
+ #
155
+ def self.from_url(source)
156
+ url = java.net.URL.new(source)
157
+ image = java.awt.Toolkit.default_toolkit.create_image(url)
158
+ tracker = java.awt.MediaTracker.new(java.awt.Label.new(""))
159
+ tracker.addImage(image, 0);
160
+ tracker.waitForID(0)
161
+ target = paint(BufferedImage.new(image.width, image.height, RGB)) do |g|
162
+ g.draw_image image, 0, 0, nil
163
+ end
164
+ block_given? ? yield(target) : target
165
+ rescue java.io.IOException, java.net.MalformedURLException
166
+ raise ArgumentError.new "Trouble retrieving image: #{$!.message}"
167
+ end
168
+
169
+ def self.with_image_impl(file)
170
+ buffered_image = ImageIO.read(file)
171
+ buffered_image ? ImageVoodoo.new(buffered_image) : nil
172
+ end
173
+
174
+ def self.with_bytes_impl(bytes)
175
+ ImageVoodoo.new ImageIO.read(ByteArrayInputStream.new(bytes))
176
+ end
177
+
178
+ private
179
+
180
+ #
181
+ # Converts a RGB hex value into a java.awt.Color object or dies trying
182
+ # with an ArgumentError.
183
+ #
184
+ def hex_to_color(rgb)
185
+ raise ArgumentError.new "hex rrggbb needed" if rgb !~ /[[:xdigit:]]{6,6}/
186
+
187
+ java.awt.Color.new(rgb[0,2].to_i(16), rgb[2,2].to_i(16), rgb[4,2].to_i(16))
188
+ end
189
+
190
+ #
191
+ # Determines the best colorspace for a new image based on whether the
192
+ # existing image contains an alpha channel or not.
193
+ #
194
+ def color_type
195
+ @src.color_model.has_alpha ? ARGB : RGB
196
+ end
197
+
198
+ #
199
+ # Make a duplicate of the underlying Java src image
200
+ #
201
+ def dup_src
202
+ BufferedImage.new width, height, color_type
203
+ end
204
+
205
+ #
206
+ # Do simple AWT operation transformation to target.
207
+ #
208
+ def transform(operation, target=dup_src)
209
+ paint(target) do |g|
210
+ g.draw_image(@src, 0, 0, nil)
211
+ g.draw_image(operation.filter(target, nil), 0, 0, nil)
212
+ end
213
+ end
214
+
215
+ #
216
+ # DRY up drawing setup+teardown
217
+ #
218
+ def paint(src=dup_src)
219
+ yield src.graphics
220
+ src.graphics.dispose
221
+ ImageVoodoo.new src
222
+ end
223
+ end
@@ -0,0 +1,53 @@
1
+ class ImageVoodoo
2
+ java_import com.google.appengine.api.images.Image
3
+ java_import com.google.appengine.api.images.ImagesService
4
+ java_import com.google.appengine.api.images.ImagesServiceFactory
5
+ java_import com.google.appengine.api.images.Transform
6
+
7
+ ImageService = ImagesServiceFactory.images_service
8
+
9
+ # Value Add methods for this backend
10
+
11
+ def i_am_feeling_lucky
12
+ transform(ImagesServiceFactory.make_im_feeling_lucky)
13
+ end
14
+
15
+ # Implementations of standard features
16
+
17
+ def flip_horizontally_impl
18
+ transform(ImagesServiceFactory.make_horizontal_flip)
19
+ end
20
+
21
+ def flip_vertically_impl
22
+ transform(ImagesServiceFactory.make_vertical_flip)
23
+ end
24
+
25
+ def resize_impl(width, height)
26
+ transform(ImagesServiceFactory.make_resize(width, height))
27
+ end
28
+
29
+ def with_crop_impl(left, top, right, bottom)
30
+ transform(ImagesServiceFactory.make_crop(left, top, right, bottom))
31
+ end
32
+
33
+ def self.with_bytes_impl(bytes)
34
+ ImageVoodoo.new ImageServicesFactory.make_image(bytes)
35
+ end
36
+
37
+ private
38
+
39
+ def from_java_bytes
40
+ String.from_java_bytes @src.image_data
41
+ end
42
+
43
+ #
44
+ # Make a duplicate of the underlying src image
45
+ #
46
+ def dup_src
47
+ ImageServicesFactory.make_image(from_java_bytes)
48
+ end
49
+
50
+ def transform(transform, target=dup_src)
51
+ ImageService.apply_transform(transform, target)
52
+ end
53
+ end
@@ -1,3 +1,3 @@
1
1
  class ImageVoodoo
2
- VERSION = "0.5"
2
+ VERSION = "0.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_voodoo
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.5"
4
+ version: "0.6"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Enebo, Charles Nutter and JRuby contributors
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-13 00:00:00 -05:00
12
+ date: 2009-06-17 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,6 +31,8 @@ files:
31
31
  - LICENSE.txt
32
32
  - lib/image_science.rb
33
33
  - lib/image_voodoo
34
+ - lib/image_voodoo/awt.rb
35
+ - lib/image_voodoo/gae.rb
34
36
  - lib/image_voodoo/version.rb
35
37
  - lib/image_voodoo.rb
36
38
  - samples/bench.rb