image_voodoo 0.2 → 0.3

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.
data/README.txt CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  == DESCRIPTION:
4
4
 
5
- ImageVoodoo is an Image manipulation library with a ImageScience-compatible API for
6
- JRuby.
5
+ ImageVoodoo is an Image manipulation library with a ImageScience-compatible API
6
+ for JRuby.
7
7
 
8
8
  http://jruby-extras.rubyforge.org/image_voodoo/
9
9
 
data/bin/image_voodoo CHANGED
@@ -27,12 +27,26 @@ opts = OptionParser.new do |opts|
27
27
  opts.separator ""
28
28
  opts.separator "Actions:"
29
29
 
30
+ opts.on("-a", "--alpha color_value", "Make color transparent in image") do |c|
31
+ if c !~ /[[:xdigit:]]{6,6}/
32
+ opts.usage "color_value is rrggbb in hexidecimal format"
33
+ end
34
+ actions << lambda {|img| img.alpha(c) }
35
+ end
36
+
30
37
  opts.on("-b", "--brightness SCALE,OFFSET", "Adjust brightness") do |args|
31
38
  scale, offset = args.split(/\,/i).map {|v| v.to_f}
32
39
  opts.usage "You need to specify proper scale and offset" unless scale && offset
33
40
  actions << lambda {|img| img.adjust_brightness(scale, offset) }
34
41
  end
35
42
 
43
+ opts.on("-B" "--border WIDTH,COLOR,STYLE", "Add a simple border") do |args|
44
+ width, color, style = args.split(/\,/i)
45
+ options = {:width => width, :color => color, :style => style }
46
+
47
+ actions << lambda {|img| img.add_border(options) }
48
+ end
49
+
36
50
  opts.on("-d", "--dimensions", "Print the image dimensions") do
37
51
  actions << lambda {|img| puts "#{img.width}x#{img.height}"; img }
38
52
  end
@@ -41,6 +55,10 @@ opts = OptionParser.new do |opts|
41
55
  actions << lambda {|img| img.greyscale }
42
56
  end
43
57
 
58
+ opts.on("-h", "--flip_horizontally") do
59
+ actions << lambda {|img| img.flip_horizontally }
60
+ end
61
+
44
62
  opts.on("-n", "--negative", "Make a negative out of the image") do
45
63
  actions << lambda {|img| img.negative }
46
64
  end
@@ -53,6 +71,10 @@ opts = OptionParser.new do |opts|
53
71
  actions << lambda {|img| img.thumbnail(size) }
54
72
  end
55
73
 
74
+ opts.on("-v", "--flip_vertically") do
75
+ actions << lambda {|img| img.flip_vertically }
76
+ end
77
+
56
78
  opts.on("-p", "--preview", "Preview the image. Close the frame window",
57
79
  "to continue, or quit the application to", "abort the action pipeline") do
58
80
  actions << lambda do |img|
data/lib/image_voodoo.rb CHANGED
@@ -1,6 +1,13 @@
1
- # ImageVoodoo is an ImageScience-API-compatible image manipulation library for JRuby.
2
1
  #
3
- # Example of usage:
2
+ # = ImageVoodoo
3
+ # == Description
4
+ #
5
+ # ImageVoodoo is an ImageScience-API-compatible image manipulation library for
6
+ # JRuby.
7
+ #
8
+ # == Examples
9
+ #
10
+ # === Simple block-based examples
4
11
  #
5
12
  # ImageVoodoo.with_image(ARGV[0]) do |img|
6
13
  # img.cropped_thumbnail(100) { |img2| img2.save "CTH.jpg" }
@@ -11,6 +18,12 @@
11
18
  # img2.save "HEH.png"
12
19
  # end
13
20
  # end
21
+ #
22
+ # === Non-block return (not image_science compatible)
23
+ #
24
+ # img = ImageVoodoo.with_image(ARGV[0])
25
+ # negative_img = img.negative
26
+ #
14
27
  class ImageVoodoo
15
28
  include Java
16
29
 
@@ -23,41 +36,88 @@ class ImageVoodoo
23
36
  import java.awt.image.RescaleOp
24
37
  import java.awt.image.BufferedImage
25
38
  JFile = java.io.File
26
- BAIS = java.io.ByteArrayInputStream
27
- BAOS = java.io.ByteArrayOutputStream
39
+ import java.io.ByteArrayInputStream
40
+ import java.io.ByteArrayOutputStream
28
41
  import javax.imageio.ImageIO
29
42
  import javax.swing.JFrame
30
43
 
31
44
  NEGATIVE_OP = LookupOp.new(ByteLookupTable.new(0, (0...254).to_a.reverse.to_java(:byte)), nil)
32
45
  GREY_OP = ColorConvertOp.new(ColorSpace.getInstance(ColorSpace::CS_GRAY), nil)
33
46
 
34
- class JImagePanel < javax.swing.JPanel
35
- def initialize(image, x=0, y=0)
36
- super()
37
- @image, @x, @y = image, x, y
38
- end
39
- def image=(image)
40
- @image = image
41
- invalidate
42
- end
43
- def getPreferredSize
44
- java.awt.Dimension.new(@image.width, @image.height)
45
- end
46
- def paintComponent(graphics)
47
- graphics.drawImage(@image.to_java, @x, @y, nil)
48
- end
49
- end
50
-
51
47
  def initialize(src)
52
48
  @src = src
53
49
  end
54
50
 
55
- # Foreach pixel new_pixel = pixel * scale + offset
51
+ #
52
+ # Add a border to the image and yield/return a new image. The following
53
+ # options are supported:
54
+ # - width: How thick is the border (default: 3)
55
+ # - color: Which color is the border (in rrggbb hex value)
56
+ # - style: etched, raised, plain (default: plain)
57
+ #
58
+ def add_border(options = {})
59
+ border_width = options[:width].to_i || 2
60
+ color = hex_to_color(options[:color]) || hex_to_color("000000")
61
+ style = options[:style]
62
+ style = nil if style.to_sym == :plain
63
+ new_width, new_height = width + 2*border_width, height + 2*border_width
64
+ target = BufferedImage.new(new_width, new_height, color_type)
65
+ graphics = target.graphics
66
+ graphics.color = color
67
+ if style
68
+ raised = style.to_sym == :raised ? true : false
69
+ graphics.fill3DRect(0, 0, new_width, new_height, raised)
70
+ else
71
+ graphics.fill_rect(0, 0, new_width, new_height)
72
+ end
73
+ graphics.draw_image(@src, nil, border_width, border_width)
74
+ graphics.dispose
75
+ target = ImageVoodoo.new target
76
+ block_given? ? yield(target) : target
77
+ end
78
+
79
+ #
80
+ # Adjusts the brightness of each pixel in image by the following formula:
81
+ # new_pixel = pixel * scale + offset
82
+ #
56
83
  def adjust_brightness(scale, offset)
57
84
  image = ImageVoodoo.new internal_transform(RescaleOp.new(scale, offset, nil))
58
85
  block_given? ? yield(image) : image
59
86
  end
60
87
 
88
+ #
89
+ # Converts rgb hex color value to an alpha value an yields/returns the new
90
+ # image.
91
+ #
92
+ def alpha(rgb)
93
+ color = hex_to_color(rgb)
94
+ target = BufferedImage.new(width, height, BufferedImage::TYPE_INT_ARGB)
95
+ graphics = target.graphics
96
+ graphics.set_composite(java.awt.AlphaComposite::Src)
97
+ graphics.draw_image(@src, nil, 0, 0)
98
+ graphics.dispose
99
+ 0.upto(height-1) do |i|
100
+ 0.upto(width-1) do |j|
101
+ target.setRGB(j, i, 0x8F1C1C) if target.getRGB(j, i) == color.getRGB
102
+ end
103
+ end
104
+ target = ImageVoodoo.new target
105
+ block_given? ? yield(target) : target
106
+ end
107
+
108
+ #
109
+ # Write current image out as a stream of bytes using provided format.
110
+ #
111
+ def bytes(format)
112
+ out = ByteArrayOutputStream.new
113
+ ImageIO.write(@src, format, out)
114
+ String.from_java_bytes(out.to_byte_array)
115
+ end
116
+
117
+ #
118
+ # Creates a square thumbnail of the image cropping the longest edge to
119
+ # match the shortest edge, resizes to size, and yields/returns the new image.
120
+ #
61
121
  def cropped_thumbnail(size)
62
122
  l, t, r, b, half = 0, 0, width, height, (width - height).abs / 2
63
123
  l, r = half, half + height if width > height
@@ -71,23 +131,51 @@ class ImageVoodoo
71
131
  end
72
132
  end
73
133
 
74
- def height
75
- @src.height
134
+ #
135
+ # Flips the image horizontally and yields/returns the new image.
136
+ #
137
+ def flip_horizontally
138
+ target = BufferedImage.new(width, height, color_type)
139
+ graphics = target.graphics
140
+ graphics.draw_image(@src, 0, 0, width, height, width, 0, 0, height, nil)
141
+ graphics.dispose
142
+ target = ImageVoodoo.new target
143
+ block_given? ? yield(target) : target
76
144
  end
77
145
 
78
- def width
79
- @src.width
146
+ #
147
+ # Flips the image vertically and yields/returns the new image.
148
+ #
149
+ def flip_vertically
150
+ target = BufferedImage.new(width, height, color_type)
151
+ graphics = target.graphics
152
+ graphics.draw_image(@src, 0, 0, width, height, 0, height, width, 0, nil)
153
+ graphics.dispose
154
+ target = ImageVoodoo.new target
155
+ block_given? ? yield(target) : target
80
156
  end
81
157
 
82
- def to_java
83
- @src
158
+ #
159
+ # Creates a grayscale version of image and yields/returns the new image.
160
+ #
161
+ def greyscale
162
+ image = ImageVoodoo.new internal_transform(GREY_OP)
163
+ block_given? ? yield(image) : image
84
164
  end
165
+ alias_method :grayscale, :greyscale
85
166
 
167
+ #
168
+ # Creates a negative and yields/returns the new image.
169
+ #
86
170
  def negative
87
171
  image = ImageVoodoo.new internal_transform(NEGATIVE_OP)
88
172
  block_given? ? yield(image) : image
89
173
  end
90
174
 
175
+ #
176
+ # Resizes the image to width and height using bicubic interpolation and
177
+ # yields/returns the new image.
178
+ #
91
179
  def resize(width, height)
92
180
  target = BufferedImage.new(width, height, color_type)
93
181
  graphics = target.graphics
@@ -105,11 +193,10 @@ class ImageVoodoo
105
193
  raise ArgumentError, ne.message
106
194
  end
107
195
 
108
- def greyscale
109
- image = ImageVoodoo.new internal_transform(GREY_OP)
110
- block_given? ? yield(image) : image
111
- end
112
-
196
+ #
197
+ # Saves the image out to path. Changing the file extension will convert
198
+ # the file type to the appropriate format.
199
+ #
113
200
  def save(file)
114
201
  format = File.extname(file)
115
202
  return false if format == ""
@@ -118,6 +205,60 @@ class ImageVoodoo
118
205
  true
119
206
  end
120
207
 
208
+ #
209
+ # Resize (scale) the current image by the provided ratio and yield/return
210
+ # the new image.
211
+ #
212
+ def scale(ratio)
213
+ new_width, new_height = (width * ratio).to_i, (height * ratio).to_i
214
+
215
+ resize(new_width, new_height) do |image|
216
+ target = ImageVoodoo.new image
217
+ block_given? ? yield(target) : target
218
+ end
219
+ end
220
+
221
+ #
222
+ # Creates a proportional thumbnail of the image scaled so its longest
223
+ # edge is resized to size and yields/returns the new image.
224
+ #
225
+ def thumbnail(size)
226
+ scale(size.to_f / (width > height ? width : height))
227
+ end
228
+
229
+ #
230
+ # Crops an image to left, top, right, and bottom and then yields/returns the
231
+ # new image.
232
+ #
233
+ def with_crop(left, top, right, bottom)
234
+ image = ImageVoodoo.new @src.get_subimage(left, top, right-left, bottom-top)
235
+ block_given? ? yield(image) : image
236
+ end
237
+
238
+ #
239
+ # A simple swing wrapper around an image voodoo object.
240
+ #
241
+ class JImagePanel < javax.swing.JPanel
242
+ def initialize(image, x=0, y=0)
243
+ super()
244
+ @image, @x, @y = image, x, y
245
+ end
246
+
247
+ def image=(image)
248
+ @image = image
249
+ invalidate
250
+ end
251
+
252
+ def getPreferredSize
253
+ java.awt.Dimension.new(@image.width, @image.height)
254
+ end
255
+
256
+ def paintComponent(graphics)
257
+ graphics.drawImage(@image.to_java, @x, @y, nil)
258
+ end
259
+ end
260
+
261
+ # Internal class for closing preview window
121
262
  class WindowClosed
122
263
  def initialize(block = nil)
123
264
  @block = block || proc { java.lang.System.exit(0) }
@@ -126,6 +267,9 @@ class ImageVoodoo
126
267
  def windowClosing(event); @block.call; end
127
268
  end
128
269
 
270
+ #
271
+ # Creates a viewable frame displaying current image within it.
272
+ #
129
273
  def preview(&block)
130
274
  frame = JFrame.new("Preview")
131
275
  frame.add_window_listener WindowClosed.new(block)
@@ -134,31 +278,11 @@ class ImageVoodoo
134
278
  frame.visible = true
135
279
  end
136
280
 
137
- def bytes(format)
138
- out = BAOS.new
139
- ImageIO.write(@src, format, out)
140
- String.from_java_bytes(out.to_byte_array)
141
- end
142
-
143
- def scale(ratio)
144
- new_width = (width * ratio).to_i
145
- new_height = (height * ratio).to_i
146
- resize(new_width, new_height) do |image|
147
- target = ImageVoodoo.new image
148
- block_given? ? yield(target) : target
149
- end
150
- end
151
-
152
- def thumbnail(size)
153
- scale(size.to_f / (width > height ? width : height))
154
- end
155
-
156
- def with_crop(left, top, right, bottom)
157
- image = ImageVoodoo.new(@src.get_subimage(left, top, right-left, bottom-top))
158
- block_given? ? yield(image) : image
159
- end
160
-
281
+ #
161
282
  # TODO: Figure out how to determine whether source has alpha or not
283
+ # Experimental: Read an image from the url source and yield/return that
284
+ # image.
285
+ #
162
286
  def self.from_url(source)
163
287
  url = java.net.URL.new(source)
164
288
  image = java.awt.Toolkit.default_toolkit.create_image(url)
@@ -175,6 +299,9 @@ class ImageVoodoo
175
299
  raise ArgumentError.new "Trouble retrieving image: #{$!.message}"
176
300
  end
177
301
 
302
+ #
303
+ # A top-level image loader opens path and then yields/returns the image.
304
+ #
178
305
  def self.with_image(file)
179
306
  readers = ImageIO.getImageReadersBySuffix(File.extname(file)[1..-1])
180
307
  raise TypeError, "unrecognized format for #{file}" unless readers.hasNext
@@ -184,18 +311,60 @@ class ImageVoodoo
184
311
  nil
185
312
  end
186
313
 
314
+ #
315
+ # A top-level image loader reads bytes and then yields/returns the image.
316
+ #
187
317
  def self.with_bytes(bytes)
188
318
  bytes = bytes.to_java_bytes if String === bytes
189
- image = ImageVoodoo.new ImageIO.read(BAIS.new(bytes))
319
+ image = ImageVoodoo.new ImageIO.read(ByteArrayInputStream.new(bytes))
190
320
  block_given? ? yield(image) : image
191
321
  end
192
322
 
323
+ #
324
+ # Returns the height of the image, in pixels.
325
+ #
326
+ def height
327
+ @src.height
328
+ end
329
+
330
+ #
331
+ # Returns the width of the image, in pixels.
332
+ #
333
+ def width
334
+ @src.width
335
+ end
336
+
337
+ #
338
+ # Returns the underlying Java BufferedImage associated with this object.
339
+ #
340
+ def to_java
341
+ @src
342
+ end
343
+
193
344
  private
345
+
346
+ #
347
+ # Converts a RGB hex value into a java.awt.Color object or dies trying
348
+ # with an ArgumentError.
349
+ #
350
+ def hex_to_color(rgb)
351
+ raise ArgumentError.new "hex rrggbb needed" if rgb !~ /[[:xdigit:]]{6,6}/
352
+
353
+ java.awt.Color.new(rgb[0,2].to_i(16), rgb[2,2].to_i(16), rgb[4,2].to_i(16))
354
+ end
355
+
356
+ #
357
+ # Determines the best colorspace for a new image based on whether the
358
+ # existing image contains an alpha channel or not.
359
+ #
194
360
  def color_type
195
361
  return BufferedImage::TYPE_INT_ARGB if @src.color_model.has_alpha
196
362
  BufferedImage::TYPE_INT_RGB
197
363
  end
198
364
 
365
+ #
366
+ # Do simple AWT operation transformation to target.
367
+ #
199
368
  def internal_transform(operation, target=BufferedImage.new(width, height, color_type))
200
369
  graphics = target.graphics
201
370
  graphics.drawImage(@src, 0, 0, nil)
@@ -1,3 +1,3 @@
1
1
  class ImageVoodoo
2
- VERSION = "0.2"
2
+ VERSION = "0.3"
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.2"
4
+ version: "0.3"
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: 2008-05-19 00:00:00 -05:00
12
+ date: 2008-06-23 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15