sobakasu-image_science 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +39 -0
- data/History.txt.orig +77 -0
- data/Manifest.txt +20 -0
- data/README.txt +73 -0
- data/Rakefile +13 -0
- data/bench.rb +63 -0
- data/bin/image_science +221 -0
- data/bin/image_science_thumb +31 -0
- data/ext/extconf.rb +87 -0
- data/ext/image_science_ext.c.in +803 -0
- data/lib/image_science.rb +157 -0
- data/spec/fixtures/pix.gif +0 -0
- data/spec/fixtures/pix.jpg +0 -0
- data/spec/fixtures/pix.png +0 -0
- data/spec/fixtures/pix2.gif +0 -0
- data/spec/fixtures/pix2.jpg +0 -0
- data/spec/fixtures/pix2.png +0 -0
- data/spec/image_science_spec.rb +531 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +97 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../ext/image_science_ext'
|
2
|
+
|
3
|
+
class ImageScience
|
4
|
+
|
5
|
+
VERSION = "1.1.3"
|
6
|
+
|
7
|
+
##
|
8
|
+
# Returns the type of the image as a string.
|
9
|
+
|
10
|
+
def self.image_type(path)
|
11
|
+
fif_to_string(file_type(path))
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Returns the type of the image as a string.
|
16
|
+
|
17
|
+
def image_type
|
18
|
+
ImageScience.fif_to_string(@file_type)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Returns the colorspace of the image as a string
|
23
|
+
|
24
|
+
def colorspace
|
25
|
+
case colortype
|
26
|
+
when 0 then depth == 1 ? 'InvertedMonochrome' : 'InvertedGrayscale'
|
27
|
+
when 1 then depth == 1 ? 'Monochrome' : 'Grayscale'
|
28
|
+
when 2 then 'RGB'
|
29
|
+
when 3 then 'Indexed'
|
30
|
+
when 4 then 'RGBA'
|
31
|
+
when 5 then 'CMYK'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def colourspace # :nodoc:
|
36
|
+
colorspace
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# alias for buffer()
|
41
|
+
|
42
|
+
def data(*args)
|
43
|
+
buffer(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# call-seq:
|
48
|
+
# thumbnail(size)
|
49
|
+
# thumbnail(size) { |image| ... }
|
50
|
+
#
|
51
|
+
# Creates a proportional thumbnail of the image scaled so its
|
52
|
+
# longest edge is resized to +size+. If a block is given, yields
|
53
|
+
# the new image, else returns true on success.
|
54
|
+
|
55
|
+
def thumbnail(size)
|
56
|
+
w, h = width, height
|
57
|
+
scale = size.to_f / (w > h ? w : h)
|
58
|
+
w = (w * scale).to_i
|
59
|
+
h = (h * scale).to_i
|
60
|
+
|
61
|
+
if block_given?
|
62
|
+
self.resize(w, h) do |image|
|
63
|
+
yield image
|
64
|
+
end
|
65
|
+
else
|
66
|
+
self.resize(w, h)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# call-seq:
|
72
|
+
# cropped_thumbnail(size)
|
73
|
+
# cropped_thumbnail(size) { |image| ... }
|
74
|
+
#
|
75
|
+
# Creates a square thumbnail of the image cropping the longest edge
|
76
|
+
# to match the shortest edge, resizes to +size+. If a block is given,
|
77
|
+
# yields the new image, else returns true on success.
|
78
|
+
|
79
|
+
def cropped_thumbnail(size) # :yields: image
|
80
|
+
w, h = width, height
|
81
|
+
l, t, r, b, half = 0, 0, w, h, (w - h).abs / 2
|
82
|
+
|
83
|
+
l, r = half, half + h if w > h
|
84
|
+
t, b = half, half + w if h > w
|
85
|
+
|
86
|
+
if block_given?
|
87
|
+
with_crop(l, t, r, b) do |img|
|
88
|
+
img.thumbnail(size) do |thumb|
|
89
|
+
yield thumb
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
crop(l, t, r, b) && thumbnail(size)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# resize the image to fit within the max_w and max_h passed in without
|
99
|
+
# changing the aspect ratio of the original image. If a block is given,
|
100
|
+
# yields the new image, else returns true on success.
|
101
|
+
|
102
|
+
def fit_within(max_w, max_h)
|
103
|
+
w, h = width, height
|
104
|
+
|
105
|
+
if w > max_w.to_i or h > max_h.to_i
|
106
|
+
|
107
|
+
w_ratio = max_w.quo(w)
|
108
|
+
h_ratio = max_h.quo(h)
|
109
|
+
|
110
|
+
if (w_ratio < h_ratio)
|
111
|
+
h = (h * w_ratio)
|
112
|
+
w = (w * w_ratio)
|
113
|
+
else
|
114
|
+
h = (h * h_ratio)
|
115
|
+
w = (w * h_ratio)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if block_given?
|
120
|
+
self.resize(w, h) do |image|
|
121
|
+
yield image
|
122
|
+
end
|
123
|
+
else
|
124
|
+
self.resize(w, h)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# call-seq:
|
129
|
+
# img[x, y] -> index
|
130
|
+
# img[x, y] -> [red, green, blue]
|
131
|
+
#
|
132
|
+
# alias for get_pixel_color
|
133
|
+
|
134
|
+
def [](x, y)
|
135
|
+
get_pixel_color(x, y)
|
136
|
+
end
|
137
|
+
|
138
|
+
# call-seq:
|
139
|
+
# img[x, y] = index
|
140
|
+
# img[x, y] = [red, green, blue]
|
141
|
+
#
|
142
|
+
# alias for set_pixel_color
|
143
|
+
|
144
|
+
def []=(x, y, *args)
|
145
|
+
set_pixel_color(x, y, *args)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def self.fif_to_string(fif)
|
151
|
+
file_types = %W{BMP ICO JPEG JNG KOALA IFF MNG PBM PBMRAW PCD PCX PGM
|
152
|
+
PGMRAW PNG PPM PPMRAW RAS TARGA TIFF WBMP PSD CUT XBM
|
153
|
+
XPM DDS GIF HDR FAXG3 SGI EXR J2K JP2}
|
154
|
+
file_types[fif]
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,531 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/image_science'
|
2
|
+
|
3
|
+
include ImageScience::ColorChannels
|
4
|
+
include ImageScience::ColorTypes
|
5
|
+
include ImageScience::ImageFilters
|
6
|
+
include ImageScience::ImageFormats
|
7
|
+
|
8
|
+
describe ImageScience do
|
9
|
+
|
10
|
+
FILE_TYPES = %W{png jpg gif bmp tif xpm}
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@path = 'spec/fixtures'
|
14
|
+
@h = @w = 50
|
15
|
+
end
|
16
|
+
|
17
|
+
after(:each) do
|
18
|
+
FILE_TYPES.each do |ext|
|
19
|
+
File.unlink tmp_image_path(ext) if File.exist? tmp_image_path(ext)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "get_version" do
|
24
|
+
it "should return the image science version" do
|
25
|
+
ImageScience.get_version.should_not be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
FILE_TYPES.each do |ext|
|
30
|
+
|
31
|
+
describe "#{ext}" do
|
32
|
+
|
33
|
+
describe "new" do
|
34
|
+
it "should create a new ImageScience object by reading a file" do
|
35
|
+
img = ImageScience.new(image_path(ext))
|
36
|
+
img.should be_kind_of(ImageScience)
|
37
|
+
[img.width, img.height].should == [@w, @h]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should create a new ImageScience object by reading image data" do
|
41
|
+
data = File.read(image_path(ext))
|
42
|
+
img = ImageScience.new(data)
|
43
|
+
img.should be_kind_of(ImageScience)
|
44
|
+
[img.width, img.height].should == [@w, @h]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "with_image" do
|
49
|
+
it "should raise an error when a file does not exist" do
|
50
|
+
lambda {
|
51
|
+
ImageScience.with_image(image_path(ext) + "nope") {}
|
52
|
+
}.should raise_error
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should fetch image dimensions" do
|
56
|
+
ImageScience.with_image image_path(ext) do |img|
|
57
|
+
img.should be_kind_of(ImageScience)
|
58
|
+
img.height.should == @h
|
59
|
+
img.width.should == @w
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "with_image_from_memory" do
|
65
|
+
it "should raise an error when an empty string is given" do
|
66
|
+
lambda {
|
67
|
+
ImageScience.with_image_from_memory("") {}
|
68
|
+
}.should raise_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "with_image_from_memory" do
|
73
|
+
it "should fetch image dimensions" do
|
74
|
+
data = File.new(image_path(ext)).binmode.read
|
75
|
+
ImageScience.with_image_from_memory data do |img|
|
76
|
+
img.should be_kind_of(ImageScience)
|
77
|
+
img.height.should == @h
|
78
|
+
img.width.should == @w
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "save" do
|
84
|
+
it "should save a new copy of an image" do
|
85
|
+
ImageScience.with_image image_path(ext) do |img|
|
86
|
+
img.save(tmp_image_path(ext)).should be_true
|
87
|
+
end
|
88
|
+
File.exists?(tmp_image_path(ext)).should be_true
|
89
|
+
|
90
|
+
ImageScience.with_image tmp_image_path(ext) do |img|
|
91
|
+
img.should be_kind_of(ImageScience)
|
92
|
+
img.height.should == @h
|
93
|
+
img.width.should == @w
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "resize" do
|
99
|
+
|
100
|
+
it "should resize an image" do
|
101
|
+
ImageScience.with_image image_path(ext) do |img|
|
102
|
+
img.resize(25, 25) do |thumb|
|
103
|
+
thumb.save(tmp_image_path(ext)).should be_true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
File.exists?(tmp_image_path(ext)).should be_true
|
108
|
+
|
109
|
+
ImageScience.with_image tmp_image_path(ext) do |img|
|
110
|
+
img.should be_kind_of(ImageScience)
|
111
|
+
img.height.should == 25
|
112
|
+
img.width.should == 25
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should resize an image given floating point dimensions" do
|
117
|
+
ImageScience.with_image image_path(ext) do |img|
|
118
|
+
img.resize(25.2, 25.7) do |thumb|
|
119
|
+
thumb.save(tmp_image_path(ext)).should be_true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
File.exists?(tmp_image_path(ext)).should be_true
|
124
|
+
|
125
|
+
ImageScience.with_image tmp_image_path(ext) do |img|
|
126
|
+
img.should be_kind_of(ImageScience)
|
127
|
+
img.height.should == 25
|
128
|
+
img.width.should == 25
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# do not accept negative or zero values for width/height
|
133
|
+
it "should raise an error if given invalid width or height" do
|
134
|
+
[ [0, 25], [25, 0], [-25, 25], [25, -25] ].each do |width, height|
|
135
|
+
lambda {
|
136
|
+
ImageScience.with_image image_path(ext) do |img|
|
137
|
+
img.resize(width, height) do |thumb|
|
138
|
+
thumb.save(tmp_image_path(ext))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
}.should raise_error
|
142
|
+
|
143
|
+
File.exists?(tmp_image_path(ext)).should be_false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should resize the image in-place if no block given" do
|
148
|
+
ImageScience.with_image image_path(ext) do |img|
|
149
|
+
img.resize(20, 20).should be_true
|
150
|
+
img.width.should == 20
|
151
|
+
img.height.should == 20
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should resize the image with the given filter" do
|
156
|
+
ImageScience.with_image image_path(ext) do |img|
|
157
|
+
img.resize(20, 20, FILTER_BILINEAR).should be_true
|
158
|
+
img.width.should == 20
|
159
|
+
img.height.should == 20
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "buffer" do
|
166
|
+
it "should return image data" do
|
167
|
+
ImageScience.with_image image_path(ext) do |img|
|
168
|
+
expected = File.size(image_path(ext))
|
169
|
+
tolerance = expected * 0.05
|
170
|
+
|
171
|
+
data = img.buffer
|
172
|
+
data.should_not be_nil
|
173
|
+
#data.length.should be_close(expected, tolerance)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should yield image data" do
|
178
|
+
ImageScience.with_image image_path(ext) do |img|
|
179
|
+
expected = File.size(image_path(ext))
|
180
|
+
tolerance = expected * 0.05
|
181
|
+
|
182
|
+
img.buffer do |data|
|
183
|
+
data.should_not be_nil
|
184
|
+
#data.length.should be_close(expected, tolerance)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should return image data in the given format" do
|
190
|
+
ImageScience.with_image image_path(ext) do |img|
|
191
|
+
[FIF_BMP, FIF_GIF, FIF_JPEG, FIF_PNG].each do |target_format|
|
192
|
+
data = img.buffer(target_format)
|
193
|
+
data.should_not be_nil
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# TODO: convert to use constants
|
199
|
+
it "should accept save flags" do
|
200
|
+
ImageScience.with_image image_path(ext) do |img|
|
201
|
+
jpg_large = img.buffer(FIF_JPEG, 0x80) # jpeg quality superb
|
202
|
+
jpg_small = img.buffer(FIF_JPEG, 0x08) # jpeg quality bad
|
203
|
+
jpg_small.length.should < jpg_large.length
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "fit_within" do
|
210
|
+
|
211
|
+
it "should resize image to fit within given dimensions (yield)" do
|
212
|
+
# 50 x 50 image -> shrink to 20, 50 -> 20, 20
|
213
|
+
ImageScience.with_image image_path(ext) do |img|
|
214
|
+
img.fit_within(20, 50) do |thumb|
|
215
|
+
thumb.width.should == 20
|
216
|
+
thumb.height.should == 20
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should resize image to fit within given dimensions (inline)" do
|
222
|
+
# 50 x 50 image -> shrink to 20, 50 -> 20, 20
|
223
|
+
ImageScience.with_image image_path(ext) do |img|
|
224
|
+
img.fit_within(20, 50)
|
225
|
+
img.width.should == 20
|
226
|
+
img.height.should == 20
|
227
|
+
end
|
228
|
+
|
229
|
+
# 100 x 50 image -> shrink to 50, 100 -> 50, 25
|
230
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
231
|
+
img.fit_within(50, 100)
|
232
|
+
img.width.should == 50
|
233
|
+
img.height.should == 25
|
234
|
+
end
|
235
|
+
|
236
|
+
# 100 x 50 image -> shrink to 150, 25 -> 50, 25
|
237
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
238
|
+
img.fit_within(150, 25)
|
239
|
+
img.width.should == 50
|
240
|
+
img.height.should == 25
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "get_pixel_color" do
|
247
|
+
expected = {
|
248
|
+
:jpg => [[62, 134, 122, 0], [0, 14, 7, 0]],
|
249
|
+
:png => [[62, 134, 121, 0], [1, 2, 2, 0]],
|
250
|
+
:gif => [[59, 135, 119, 0], [0, 2, 0, 0]],
|
251
|
+
:bmp => [[62, 134, 121, 0], [1, 2, 2, 0]],
|
252
|
+
:tif => [[62, 134, 121, 0], [1, 2, 2, 0]],
|
253
|
+
:xpm => [[255, 255, 255, 0], [0, 0, 0, 0]]
|
254
|
+
}
|
255
|
+
|
256
|
+
it "should get pixel color" do
|
257
|
+
ImageScience.with_image image_path(ext) do |img|
|
258
|
+
rgb = img.get_pixel_color(10,7)
|
259
|
+
rgb.should_not be_nil
|
260
|
+
rgb.should == expected[ext.to_sym][0]
|
261
|
+
|
262
|
+
rgb = img.get_pixel_color(24,0)
|
263
|
+
rgb.should_not be_nil
|
264
|
+
rgb.should == expected[ext.to_sym][1]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should get pixel color with []" do
|
269
|
+
ImageScience.with_image image_path(ext) do |img|
|
270
|
+
rgb = img[10,7]
|
271
|
+
rgb.should_not be_nil
|
272
|
+
rgb.should == expected[ext.to_sym][0]
|
273
|
+
|
274
|
+
rgb = img[24,0]
|
275
|
+
rgb.should_not be_nil
|
276
|
+
rgb.should == expected[ext.to_sym][1]
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "set_pixel_color" do
|
282
|
+
it "should set pixel color" do
|
283
|
+
ImageScience.with_image image_path(ext) do |img|
|
284
|
+
if img.colortype == FIC_PALETTE
|
285
|
+
img.set_pixel_color(10, 7, 3).should be_true
|
286
|
+
img[25, 25] = 4
|
287
|
+
else
|
288
|
+
img.set_pixel_color(10, 7, [100, 20, 50]).should be_true
|
289
|
+
img.set_pixel_color(25, 25, 100, 20, 50).should be_true
|
290
|
+
img.set_pixel_color(25, 25, [100, 20, 50, 0]).should be_true
|
291
|
+
img.set_pixel_color(25, 25, 100, 20, 50, 0).should be_true
|
292
|
+
img[25, 25] = [100, 20, 50, 0];
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "thumbnail" do
|
299
|
+
# Note: pix2 is 100x50
|
300
|
+
it "should create a proportional thumbnail" do
|
301
|
+
thumbnail_created = false
|
302
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
303
|
+
img.thumbnail(30) do |thumb|
|
304
|
+
thumb.should_not be_nil
|
305
|
+
thumb.width.should == 30
|
306
|
+
thumb.height.should == thumb.width / 2 # half of width
|
307
|
+
thumbnail_created = true
|
308
|
+
end
|
309
|
+
end
|
310
|
+
thumbnail_created.should be_true
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should create a proportional thumbnail in-place if no block given" do
|
314
|
+
thumbnail_created = false
|
315
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
316
|
+
img.thumbnail(30)
|
317
|
+
img.width.should == 30
|
318
|
+
img.height.should == img.width / 2 # half of width
|
319
|
+
thumbnail_created = true
|
320
|
+
end
|
321
|
+
thumbnail_created.should be_true
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "cropped_thumbnail" do
|
326
|
+
# Note: pix2 is 100x50
|
327
|
+
it "should create a square thumbnail" do
|
328
|
+
thumbnail_created = false
|
329
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
330
|
+
img.cropped_thumbnail(30) do |thumb|
|
331
|
+
thumb.should_not be_nil
|
332
|
+
thumb.width.should == 30
|
333
|
+
thumb.height.should == 30 # same as width
|
334
|
+
thumbnail_created = true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
thumbnail_created.should be_true
|
338
|
+
end
|
339
|
+
|
340
|
+
it "should create a square thumbnail in-place if no block given" do
|
341
|
+
thumbnail_created = false
|
342
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
343
|
+
img.cropped_thumbnail(30)
|
344
|
+
img.width.should == 30
|
345
|
+
img.height.should == 30 # same as width
|
346
|
+
thumbnail_created = true
|
347
|
+
end
|
348
|
+
thumbnail_created.should be_true
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe "crop" do
|
353
|
+
it "should crop the image in-place if no block given" do
|
354
|
+
ImageScience.with_image image_path(ext) do |img|
|
355
|
+
img.crop(0, 0, 25, 20).should be_true
|
356
|
+
img.width.should == 25
|
357
|
+
img.height.should == 20
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# image_type calls ImageScience.file_type, converts to string.
|
363
|
+
# allow calling as a class or instance method
|
364
|
+
describe "image_type" do
|
365
|
+
expected = {
|
366
|
+
'gif' => 'GIF',
|
367
|
+
'jpg' => 'JPEG',
|
368
|
+
'png' => 'PNG',
|
369
|
+
'bmp' => 'BMP',
|
370
|
+
'tif' => 'TIFF',
|
371
|
+
'xpm' => 'XPM'
|
372
|
+
}
|
373
|
+
it "should return the image type (class method)" do
|
374
|
+
ImageScience.image_type(image_path(ext)).should == expected[ext]
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should return the image type (instance method)" do
|
378
|
+
ImageScience.with_image image_path(ext) do |img|
|
379
|
+
img.image_type.should == expected[ext]
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# colorspace calls img.colortype & img.depth, converts to string
|
385
|
+
describe "colorspace" do
|
386
|
+
it "should return the color space" do
|
387
|
+
expected = {
|
388
|
+
'gif' => 'Indexed',
|
389
|
+
'jpg' => 'RGB',
|
390
|
+
'png' => 'RGB',
|
391
|
+
'bmp' => 'RGB',
|
392
|
+
'tif' => 'RGB',
|
393
|
+
'xpm' => 'Indexed'
|
394
|
+
}
|
395
|
+
ImageScience.with_image image_path(ext) do |img|
|
396
|
+
img.colorspace.should == expected[ext]
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
describe "depth" do
|
402
|
+
it "should return the BPP of the image" do
|
403
|
+
expected = {
|
404
|
+
'gif' => 8,
|
405
|
+
'jpg' => 24,
|
406
|
+
'png' => 24,
|
407
|
+
'bmp' => 24,
|
408
|
+
'tif' => 24,
|
409
|
+
'xpm' => 8
|
410
|
+
}
|
411
|
+
ImageScience.with_image image_path(ext) do |img|
|
412
|
+
img.depth.should == expected[ext]
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe "adjust_gamma" do
|
418
|
+
it "should perform gamma correction" do
|
419
|
+
ImageScience.with_image image_path(ext) do |img|
|
420
|
+
# darken image
|
421
|
+
img.adjust_gamma(0.5).should be_true
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
describe "adjust_brightness" do
|
427
|
+
it "should adjust brightness" do
|
428
|
+
ImageScience.with_image image_path(ext) do |img|
|
429
|
+
# 50% brighter
|
430
|
+
img.adjust_brightness(50).should be_true
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe "adjust_contrast" do
|
436
|
+
it "should adjust contrast" do
|
437
|
+
ImageScience.with_image image_path(ext) do |img|
|
438
|
+
# 50% less contrast
|
439
|
+
img.adjust_contrast(-50).should be_true
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
describe "invert" do
|
445
|
+
it "should invert pixel data" do
|
446
|
+
ImageScience.with_image image_path(ext) do |img|
|
447
|
+
# 50% less contrast
|
448
|
+
img.invert.should be_true
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
describe "histogram" do
|
454
|
+
it "should compute the image histogram" do
|
455
|
+
ImageScience.with_image image_path(ext) do |img|
|
456
|
+
h = img.histogram
|
457
|
+
h.should be_kind_of(Array)
|
458
|
+
h.length.should == 256
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
it "should compute the image histogram for a given channel" do
|
463
|
+
ImageScience.with_image image_path(ext) do |img|
|
464
|
+
[FICC_RED, FICC_GREEN, FICC_BLUE].each do |channel|
|
465
|
+
h = img.histogram(channel)
|
466
|
+
h.should be_kind_of(Array)
|
467
|
+
h.length.should == 256
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe "rotate" do
|
474
|
+
|
475
|
+
# test Rotate
|
476
|
+
it "should rotate the image 90 degrees" do
|
477
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
478
|
+
w, h = img.width, img.height
|
479
|
+
img.rotate(90)
|
480
|
+
# width & height reversed
|
481
|
+
img.width.should == h
|
482
|
+
img.height.should == w
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
# test RotateEx
|
487
|
+
it "should rotate the image 120 degrees about an origin" do
|
488
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
489
|
+
w, h = img.width, img.height
|
490
|
+
img.rotate(120, 0, 0, 20, 20)
|
491
|
+
# width & height unchanged
|
492
|
+
img.width.should == w
|
493
|
+
img.height.should == h
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
end
|
498
|
+
|
499
|
+
describe "flip_horizontal" do
|
500
|
+
it "should flip the image horizontally" do
|
501
|
+
ImageScience.with_image image_path(ext) do |img|
|
502
|
+
img.flip_horizontal.should be_true
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
describe "flip_vertical" do
|
508
|
+
it "should flip the image vertically" do
|
509
|
+
ImageScience.with_image image_path(ext) do |img|
|
510
|
+
img.flip_vertical.should be_true
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
private
|
519
|
+
|
520
|
+
def image_path(extension, basename = "pix")
|
521
|
+
raise "extension required" unless extension
|
522
|
+
File.join(@path, "#{basename}.#{extension}")
|
523
|
+
end
|
524
|
+
|
525
|
+
def tmp_image_path(extension, basename = "pix")
|
526
|
+
raise "extension required" unless extension
|
527
|
+
File.join(@path, "#{basename}-tmp.#{extension}")
|
528
|
+
end
|
529
|
+
|
530
|
+
end
|
531
|
+
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|