sobakasu-image_science 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'cp-deployment'