axon 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/CHANGELOG.rdoc +9 -3
  2. data/README.rdoc +29 -36
  3. data/Rakefile +26 -21
  4. data/TODO.rdoc +1 -6
  5. data/ext/axon/axon.c +6 -15
  6. data/ext/axon/extconf.rb +19 -9
  7. data/ext/axon/interpolation.c +147 -0
  8. data/ext/axon/jpeg.c +1207 -0
  9. data/ext/axon/png.c +542 -0
  10. data/lib/axon.rb +235 -32
  11. data/lib/axon/cropper.rb +80 -18
  12. data/lib/axon/fit.rb +69 -19
  13. data/lib/axon/generators.rb +109 -0
  14. data/lib/axon/scalers.rb +160 -0
  15. data/test/helper.rb +151 -6
  16. data/test/reader_tests.rb +37 -82
  17. data/test/scaler_tests.rb +102 -0
  18. data/test/stress_helper.rb +58 -0
  19. data/test/stress_tests.rb +8 -5
  20. data/test/test_bilinear_scaler.rb +60 -2
  21. data/test/test_cropper.rb +68 -1
  22. data/test/test_fit.rb +35 -0
  23. data/test/test_generators.rb +21 -0
  24. data/test/test_image.rb +61 -0
  25. data/test/test_jpeg_reader.rb +96 -94
  26. data/test/test_jpeg_writer.rb +95 -8
  27. data/test/test_nearest_neighbor_scaler.rb +28 -4
  28. data/test/test_png_reader.rb +12 -8
  29. data/test/test_png_writer.rb +8 -6
  30. data/test/writer_tests.rb +129 -111
  31. metadata +71 -128
  32. data/.gemtest +0 -0
  33. data/ext/axon/bilinear_interpolation.c +0 -115
  34. data/ext/axon/interpolation.h +0 -7
  35. data/ext/axon/jpeg_common.c +0 -118
  36. data/ext/axon/jpeg_common.h +0 -37
  37. data/ext/axon/jpeg_native_writer.c +0 -248
  38. data/ext/axon/jpeg_reader.c +0 -774
  39. data/ext/axon/nearest_neighbor_interpolation.c +0 -50
  40. data/ext/axon/png_common.c +0 -21
  41. data/ext/axon/png_common.h +0 -18
  42. data/ext/axon/png_native_writer.c +0 -166
  43. data/ext/axon/png_reader.c +0 -381
  44. data/lib/axon/axon.so +0 -0
  45. data/lib/axon/bilinear_scaler.rb +0 -60
  46. data/lib/axon/jpeg_writer.rb +0 -41
  47. data/lib/axon/nearest_neighbor_scaler.rb +0 -39
  48. data/lib/axon/png_writer.rb +0 -35
  49. data/lib/axon/scaler.rb +0 -41
  50. data/lib/axon/solid.rb +0 -23
  51. data/test/_test_readme.rb +0 -34
  52. data/test/test_exif.rb +0 -39
  53. data/test/test_generator.rb +0 -10
  54. data/test/test_icc.rb +0 -18
  55. data/test/test_jpeg.rb +0 -9
  56. data/test/test_png.rb +0 -9
@@ -0,0 +1,102 @@
1
+ module Axon
2
+ module ScalerTests
3
+ def test_dimensions
4
+ [
5
+ [7, 8],
6
+ [71, 82],
7
+ [@image.width, @image.height],
8
+ [1, 1]
9
+ ].each do |dims|
10
+ im = Solid.new(100, 200)
11
+
12
+ w, h = dims[0], dims[1]
13
+
14
+ s = @scalerclass.new(im, w, h)
15
+ assert_image_dimensions(s, w, h)
16
+ end
17
+ end
18
+
19
+ def test_bad_dimensions
20
+ [ [0, 0], [0, 1], [1, 0], [-1, -3] ].each do |dims|
21
+ w, h = dims[0], dims[1]
22
+
23
+ assert_raises ArgumentError do
24
+ @scalerclass.new(@image, w, h).gets
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_scalar
30
+ [1, 2, 3, 1.1, 2.1, 0.9, 0.1].each do |scale|
31
+ im = Solid.new(100, 200)
32
+ width = (im.width * scale).to_i
33
+ height = (im.height * scale).to_i
34
+
35
+ s = @scalerclass.new(im, width, height)
36
+ assert_image_dimensions(s, width, height)
37
+ end
38
+ end
39
+
40
+ def test_bad_scalar
41
+ [0, 0.0, -1, -0.1].each do |scale|
42
+ assert_raises ArgumentError do
43
+ @scalerclass.new(@image, scale).gets
44
+ end
45
+ end
46
+ end
47
+
48
+ def scale_test(*coords)
49
+ cache = Repeater.new(Noise.new 100, 200)
50
+
51
+ [1.0, 5.0, 0.3, 2.8].each do |scale|
52
+ width = (cache.width * scale).to_i
53
+ height = (cache.height * scale).to_i
54
+
55
+ cache.rewind
56
+ test_scaler = @scalertestclass.new(cache, width, height)
57
+ cache.rewind
58
+ s = @scalerclass.new(cache, width, height)
59
+ resized = []
60
+ s.height.times{ resized << s.gets }
61
+
62
+ right = width - 1
63
+ bottom = height - 1
64
+
65
+ coords.each do |coord|
66
+ x = coord[0]
67
+ y = coord[1]
68
+
69
+ x = right if x < 0
70
+ y = bottom if y < 0
71
+
72
+ result = resized[y][x * 3, 3].chars.map do |c|
73
+ c.respond_to?(:ord) ? c.ord : c[0]
74
+ end
75
+
76
+ expected = test_scaler.calc(x, y)
77
+ 3.times do |i|
78
+ assert_in_delta expected[i], result[i], 1
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def test_pixel_values
85
+ scale_test [10, 10], [29, 20], [9, 15]
86
+ end
87
+
88
+ def test_edge_pixel_values
89
+ scale_test [0, 10], [29, 0], [-1, 15], [17, -1]
90
+ end
91
+
92
+ def test_corner_pixel_values
93
+ scale_test [0, 0], [-1, 0], [0, -1], [-1, -1]
94
+ end
95
+
96
+ def test_small_scaling
97
+ im = Solid.new(10, 20)
98
+ sc = @scalerclass.new(im, 2, 5)
99
+ assert_image_dimensions(sc, 2, 5)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,58 @@
1
+
2
+ class MiniTest::Unit::TestCase
3
+ WARMUP_TESTS = 10
4
+ MAX_TEST_TIME = 3
5
+ MAX_TESTS = 3000
6
+
7
+ def vmsize
8
+ GC.start
9
+ GC.disable
10
+ vms = IO.read("/proc/#{$$}/status")[/^VmSize:\s+(\d+)/, 1].to_i
11
+ GC.enable
12
+ vms
13
+ end
14
+
15
+ def vmdelta
16
+ start_vmsize = vmsize
17
+ yield
18
+ vmsize - start_vmsize
19
+ end
20
+
21
+ def limited_loop
22
+ start_time = Time.now
23
+ test_counter = 0
24
+
25
+ until Time.now - start_time > MAX_TEST_TIME || test_counter > MAX_TESTS do
26
+ yield
27
+ test_counter += 1
28
+ end
29
+
30
+ return test_counter
31
+ end
32
+
33
+ def mem_loop
34
+ result = yield
35
+ WARMUP_TESTS.times{ yield }
36
+ num_tests = 0
37
+
38
+ delta = vmdelta do
39
+ num_tests = limited_loop do
40
+ yield
41
+ end
42
+ end
43
+
44
+ # The idea here is that a legitimate memory leak will leak at least one
45
+ # byte per iteration.
46
+ if delta >= num_tests
47
+ puts "\n#{self.class} #{__name__}: #{delta}"
48
+ end
49
+
50
+ result
51
+ end
52
+
53
+ alias_method :old_run, :run
54
+
55
+ def run(*args)
56
+ mem_loop{ old_run(*args) }
57
+ end
58
+ end
data/test/stress_tests.rb CHANGED
@@ -4,9 +4,12 @@ module Axon
4
4
  class TestStressCases < AxonTestCase
5
5
  def setup
6
6
  @velvet = "\x0A\x14\x69"
7
- @image = Generator::Solid.new 1000, 1500, @velvet
8
- @jpeg_data = @image.to_jpeg.data
9
- @readerclass = JPEGReader
7
+ @image = Solid.new 1000, 1500, @velvet
8
+
9
+ io = StringIO.new
10
+ JPEG.write(@image, io)
11
+ @jpeg_data = io.string
12
+ @readerclass = JPEG::Reader
10
13
  end
11
14
 
12
15
  class RandomRaiseIO
@@ -15,7 +18,7 @@ module Axon
15
18
  @die_at = rand(source.size)
16
19
  end
17
20
 
18
- def read
21
+ def read(*args)
19
22
  if @die_at < 2
20
23
  raise 'random death!'
21
24
  end
@@ -40,7 +43,7 @@ module Axon
40
43
  @sequence = sequence
41
44
  end
42
45
 
43
- def read
46
+ def read(*args)
44
47
  if !@sequence || @sequence.empty?
45
48
  raise "random death!"
46
49
  @sequence = nil
@@ -1,9 +1,67 @@
1
1
  require 'helper'
2
+ require 'scaler_tests'
2
3
 
3
4
  module Axon
4
5
  class TestBilinearScaler < AxonTestCase
5
- def test_scaling
6
- @image.scale_bilinear(0.7).write_jpeg(@io_out)
6
+ include ScalerTests
7
+
8
+ def setup
9
+ super
10
+ @scalerclass = BilinearScaler
11
+ @scalertestclass = TestBilinearScaler
12
+ end
13
+
14
+ class TestBilinearScaler
15
+ def initialize(original, width, height)
16
+ @original = original
17
+ @scale_x_inv = original.width / width.to_f
18
+ @scale_y_inv = original.height / height.to_f
19
+
20
+ # pad right and bottom with one pixel
21
+ @data = []
22
+ @original.height.times do
23
+ sl = @original.gets
24
+ @data << sl + sl[-@original.components, @original.components]
25
+ end
26
+ @data << @data.last
27
+ end
28
+
29
+ def calc(x, y)
30
+ sample_x = x * @scale_x_inv
31
+ sample_y = y * @scale_y_inv
32
+
33
+ sample_x_i = sample_x.floor
34
+ sample_y_i = sample_y.floor
35
+
36
+ tx = sample_x - sample_x_i
37
+ ty = sample_y - sample_y_i
38
+
39
+ ret = []
40
+
41
+ cmp = @original.components
42
+
43
+ cmp.times do |i|
44
+ smp_x = sample_x_i * @original.components + i
45
+
46
+ c00 = @data[sample_y_i][smp_x]
47
+ c10 = @data[sample_y_i][smp_x + cmp]
48
+ c01 = @data[sample_y_i + 1][smp_x]
49
+ c11 = @data[sample_y_i + 1][smp_x + cmp]
50
+
51
+ unless RUBY_VERSION == '1.8.7'
52
+ c00 = c00.ord
53
+ c10 = c10.ord
54
+ c01 = c01.ord
55
+ c11 = c11.ord
56
+ end
57
+
58
+ a = (1 - tx) * c00 + tx * c10
59
+ b = (1 - tx) * c01 + tx * c11
60
+ ret << ((1 - ty) * a + ty * b).floor
61
+ end
62
+
63
+ ret
64
+ end
7
65
  end
8
66
  end
9
67
  end
data/test/test_cropper.rb CHANGED
@@ -3,7 +3,74 @@ require 'helper'
3
3
  module Axon
4
4
  class TestCropper < AxonTestCase
5
5
  def test_crop
6
- @image.scale_nearest_neighbor(40).crop(320, 123).write_jpeg(@io_out)
6
+ i = Solid.new(200, 100)
7
+ c = Cropper.new(i, 4, 55)
8
+
9
+ assert_equal 4, c.width
10
+ assert_equal 55, c.height
11
+
12
+ 55.times do |i|
13
+ assert_equal i, c.lineno
14
+ assert_equal 4 * c.components, c.gets.size
15
+ end
16
+
17
+ assert_equal nil, c.gets
18
+ assert_equal 55, c.lineno
19
+ end
20
+
21
+ def test_crop_too_tall
22
+ i = Solid.new(200, 100)
23
+ c = Cropper.new(i, 4, 133)
24
+ assert_image_dimensions(c, 4, 100)
25
+ end
26
+
27
+ def test_crop_too_wide
28
+ i = Solid.new(200, 100)
29
+ c = Cropper.new(i, 233, 33)
30
+ assert_image_dimensions(c, 200, 33)
31
+ end
32
+
33
+ def test_crop_negative_values
34
+ i = Solid.new(200, 100)
35
+
36
+ assert_raises(ArgumentError) do
37
+ Cropper.new(i, -233, 33)
38
+ end
39
+
40
+ assert_raises(ArgumentError) do
41
+ Cropper.new(i, 100, -33)
42
+ end
43
+
44
+ assert_raises(ArgumentError) do
45
+ Cropper.new(i, 0, 23)
46
+ end
47
+ end
48
+
49
+ def test_crop_with_offset
50
+ i = Noise.new(100, 200)
51
+
52
+ data = []
53
+ i.height.times{ data << i.gets }
54
+ c = Cropper.new(ArrayWrapper.new(data), 42, 51, 22, 31)
55
+
56
+ assert_equal 42, c.width
57
+ assert_equal 51, c.height
58
+
59
+ 51.times do |i|
60
+ assert_equal i, c.lineno
61
+ sl = c.gets
62
+ assert_equal 42 * c.components, sl.size
63
+ assert_equal data[i + 31][22 * c.components], sl[0]
64
+ end
65
+
66
+ assert_equal nil, c.gets
67
+ assert_equal 51, c.lineno
68
+ end
69
+
70
+ def test_crop_with_offset_oob
71
+ i = Solid.new(200, 100)
72
+ c = Cropper.new(i, 30, 40, 220, 10)
73
+ assert_nil c.gets
7
74
  end
8
75
  end
9
76
  end
data/test/test_fit.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'helper'
2
+
3
+ module Axon
4
+ class TestFit < AxonTestCase
5
+ def test_width_determines_downscaling
6
+ im = Solid.new(10, 20)
7
+ r = Fit.new(im, 5, 20)
8
+ assert_image_dimensions(r, 5, 10)
9
+ end
10
+
11
+ def test_height_determines_downscaling
12
+ im = Solid.new(10, 20)
13
+ r = Fit.new(im, 20, 5)
14
+ assert_image_dimensions(r, 2, 5)
15
+ end
16
+
17
+ def test_width_determines_upscaling
18
+ im = Solid.new(10, 20)
19
+ r = Fit.new(im, 100, 900)
20
+ assert_image_dimensions(r, 100, 200)
21
+ end
22
+
23
+ def test_height_determines_upscaling
24
+ im = Solid.new(10, 20)
25
+ r = Fit.new(im, 1000, 200)
26
+ assert_image_dimensions(r, 100, 200)
27
+ end
28
+
29
+ def test_dimensions_unchanged
30
+ im = Solid.new(10, 20)
31
+ r = Fit.new(im, 10, 20)
32
+ assert_image_dimensions(r, 10, 20)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ require "helper"
2
+
3
+ module Axon
4
+ class TestGenerators < AxonTestCase
5
+ def test_solid_color
6
+ g = Solid.new 10, 15, "\x0A\x14\x69"
7
+ sl = g.gets
8
+ assert_equal "\x0A\x14\x69", sl[9, 3]
9
+ end
10
+
11
+ def test_solid_dimensions
12
+ g = Solid.new(23, 33)
13
+ assert_image_dimensions(g, 23, 33)
14
+ end
15
+
16
+ def test_noise_dimensions
17
+ im = Noise.new(100, 200, :components => 1, :color_model => :GRAYSCALE)
18
+ assert_image_dimensions(im, 100, 200)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ require 'helper'
2
+ require 'tempfile'
3
+
4
+ module Axon
5
+ class TestImage < AxonTestCase
6
+ def setup
7
+ super
8
+ io = StringIO.new
9
+ JPEG.write(Solid.new(10, 20), io)
10
+ @jpeg_data = io.string
11
+
12
+ io = StringIO.new
13
+ PNG.write(Solid.new(10, 20), io)
14
+ @png_data = io.string
15
+ end
16
+
17
+ def test_jpeg_helper_io
18
+ image = Axon.jpeg(StringIO.new(@jpeg_data))
19
+ assert_image_dimensions(image, 10, 20)
20
+ end
21
+
22
+ def test_jpeg_helper_stringdata
23
+ image = Axon.jpeg(@jpeg_data)
24
+ assert_image_dimensions(image, 10, 20)
25
+ end
26
+
27
+ def test_png_helper_io
28
+ image = Axon.png(StringIO.new(@png_data))
29
+ assert_image_dimensions(image, 10, 20)
30
+ end
31
+
32
+ def test_jpeg_helper_stringdata
33
+ image = Axon.png(@png_data)
34
+ assert_image_dimensions(image, 10, 20)
35
+ end
36
+
37
+ def test_bilinear
38
+ image = Axon.jpeg(@jpeg_data)
39
+ image.scale_bilinear(50, 75)
40
+ assert_image_dimensions(image, 50, 75)
41
+ end
42
+
43
+ def test_nearest
44
+ image = Axon.jpeg(@jpeg_data)
45
+ image.scale_nearest(50, 75)
46
+ assert_image_dimensions(image, 50, 75)
47
+ end
48
+
49
+ def test_fit
50
+ image = Axon.jpeg(@jpeg_data)
51
+ image.fit(50, 80)
52
+ assert_image_dimensions(image, 40, 80)
53
+ end
54
+
55
+ def test_crop
56
+ image = Axon.jpeg(@jpeg_data)
57
+ image.crop(5, 10, 6, 2)
58
+ assert_image_dimensions(image, 4, 10)
59
+ end
60
+ end
61
+ end