axon 0.0.2 → 0.1.0

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.
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