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 +2 -2
- data/bin/image_voodoo +22 -0
- data/lib/image_voodoo.rb +227 -58
- data/lib/image_voodoo/version.rb +1 -1
- metadata +2 -2
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
|
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
|
-
#
|
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
|
-
|
27
|
-
|
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
|
-
#
|
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
|
-
|
75
|
-
|
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
|
-
|
79
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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(
|
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)
|
data/lib/image_voodoo/version.rb
CHANGED
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.
|
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-
|
12
|
+
date: 2008-06-23 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|