axon 0.0.1
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/.gemtest +0 -0
- data/CHANGELOG.rdoc +3 -0
- data/README.rdoc +104 -0
- data/Rakefile +30 -0
- data/TODO.rdoc +12 -0
- data/ext/axon/axon.c +20 -0
- data/ext/axon/bilinear_interpolation.c +115 -0
- data/ext/axon/extconf.rb +21 -0
- data/ext/axon/iccjpeg.c +248 -0
- data/ext/axon/iccjpeg.h +73 -0
- data/ext/axon/interpolation.h +7 -0
- data/ext/axon/jpeg_common.c +118 -0
- data/ext/axon/jpeg_common.h +37 -0
- data/ext/axon/jpeg_native_writer.c +248 -0
- data/ext/axon/jpeg_reader.c +774 -0
- data/ext/axon/nearest_neighbor_interpolation.c +50 -0
- data/ext/axon/png_common.c +21 -0
- data/ext/axon/png_common.h +18 -0
- data/ext/axon/png_native_writer.c +166 -0
- data/ext/axon/png_reader.c +381 -0
- data/lib/axon/axon.so +0 -0
- data/lib/axon/bilinear_scaler.rb +60 -0
- data/lib/axon/cropper.rb +35 -0
- data/lib/axon/fit.rb +67 -0
- data/lib/axon/jpeg_writer.rb +41 -0
- data/lib/axon/nearest_neighbor_scaler.rb +39 -0
- data/lib/axon/png_writer.rb +35 -0
- data/lib/axon/scaler.rb +41 -0
- data/lib/axon/solid.rb +23 -0
- data/lib/axon.rb +45 -0
- data/test/_test_readme.rb +34 -0
- data/test/helper.rb +17 -0
- data/test/reader_tests.rb +115 -0
- data/test/stress_tests.rb +71 -0
- data/test/test_bilinear_scaler.rb +9 -0
- data/test/test_cropper.rb +9 -0
- data/test/test_exif.rb +39 -0
- data/test/test_generator.rb +10 -0
- data/test/test_icc.rb +18 -0
- data/test/test_jpeg.rb +9 -0
- data/test/test_jpeg_reader.rb +109 -0
- data/test/test_jpeg_writer.rb +26 -0
- data/test/test_nearest_neighbor_scaler.rb +13 -0
- data/test/test_png.rb +9 -0
- data/test/test_png_reader.rb +15 -0
- data/test/test_png_writer.rb +13 -0
- data/test/writer_tests.rb +179 -0
- metadata +148 -0
    
        data/lib/axon/cropper.rb
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module Axon
         | 
| 2 | 
            +
              class Cropper
         | 
| 3 | 
            +
                include Enumerable
         | 
| 4 | 
            +
                include Image
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                attr_reader :image, :width, :height, :components, :color_model
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(image, width, height, x_offset=nil, y_offset=nil)
         | 
| 9 | 
            +
                  @image = image
         | 
| 10 | 
            +
                  @width = width
         | 
| 11 | 
            +
                  @height = height
         | 
| 12 | 
            +
                  @x_offset = x_offset || 0
         | 
| 13 | 
            +
                  @y_offset = y_offset || 0
         | 
| 14 | 
            +
                  @components = image.components
         | 
| 15 | 
            +
                  @color_model = image.color_model
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def each
         | 
| 19 | 
            +
                  sl_width = @width * @components
         | 
| 20 | 
            +
                  sl_offset = @x_offset * @components
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  @image.each_with_index do |orig_sl, i|
         | 
| 23 | 
            +
                    next if i < @y_offset
         | 
| 24 | 
            +
                    yield orig_sl[sl_offset, sl_width]
         | 
| 25 | 
            +
                    break if i == @height - 1
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              module Image
         | 
| 31 | 
            +
                def crop(*args)
         | 
| 32 | 
            +
                  Cropper.new(self, *args)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/axon/fit.rb
    ADDED
    
    | @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            module Axon
         | 
| 2 | 
            +
              class Fit
         | 
| 3 | 
            +
                include Image
         | 
| 4 | 
            +
                include Enumerable
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(source, width, height)
         | 
| 7 | 
            +
                  @source, @fit_width, @fit_height = source, width, height
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def components
         | 
| 11 | 
            +
                  @source.components
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def color_model
         | 
| 15 | 
            +
                  @source.color_model
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def width
         | 
| 19 | 
            +
                  @source.width * calc_fit_ratio
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def height
         | 
| 23 | 
            +
                  @source.height * calc_fit_ratio
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def each
         | 
| 27 | 
            +
                  r = calc_fit_ratio
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  if r > 1
         | 
| 30 | 
            +
                    scaler = NearestNeighborScaler.new(@source, r)
         | 
| 31 | 
            +
                    scaler.each{ |*a| yield(*a) }
         | 
| 32 | 
            +
                  elsif r < 1
         | 
| 33 | 
            +
                    if r <= 0.5 && @source.kind_of?(JPEGReader)
         | 
| 34 | 
            +
                      @source.scale_denom = calc_jpeg_pre_shrink(r)
         | 
| 35 | 
            +
                      r = calc_fit_ratio
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                    scaler = BilinearScaler.new(@source, r)
         | 
| 38 | 
            +
                    scaler.each{ |*a| yield(*a) }
         | 
| 39 | 
            +
                  else
         | 
| 40 | 
            +
                    @source.each{ |*a| yield(*a) }
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                private
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def calc_fit_ratio
         | 
| 47 | 
            +
                  width_ratio = @fit_width.to_f / @source.width
         | 
| 48 | 
            +
                  height_ratio = @fit_height.to_f / @source.height
         | 
| 49 | 
            +
                  [width_ratio, height_ratio].min
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def calc_jpeg_pre_shrink(r)
         | 
| 53 | 
            +
                  case (1/r)
         | 
| 54 | 
            +
                  when (0...2) then nil
         | 
| 55 | 
            +
                  when (2...4) then 2
         | 
| 56 | 
            +
                  when (4...8) then 4
         | 
| 57 | 
            +
                  else 8
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              module Image
         | 
| 63 | 
            +
                def fit(*args)
         | 
| 64 | 
            +
                  Fit.new(self, *args)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            require 'stringio'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Axon
         | 
| 4 | 
            +
              class JPEGWriter
         | 
| 5 | 
            +
                include JPEGNativeWriter
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                attr_accessor :quality, :icc_profile, :exif
         | 
| 8 | 
            +
                attr_reader :io, :bufsize, :image
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(image)
         | 
| 11 | 
            +
                  @image = image
         | 
| 12 | 
            +
                  @quality = nil
         | 
| 13 | 
            +
                  @icc_profile = nil
         | 
| 14 | 
            +
                  @exif = nil
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                def write(io, bufsize = nil)
         | 
| 18 | 
            +
                  @bufsize = bufsize || 512
         | 
| 19 | 
            +
                  @io = io
         | 
| 20 | 
            +
                  jpeg_native_write
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def data
         | 
| 24 | 
            +
                  s = StringIO.new
         | 
| 25 | 
            +
                  s.set_encoding 'BINARY' if s.respond_to?(:set_encoding)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  write(s)
         | 
| 28 | 
            +
                  s.string
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              module Image
         | 
| 33 | 
            +
                def to_jpeg(*args)
         | 
| 34 | 
            +
                  JPEGWriter.new self, *args
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def write_jpeg(io)
         | 
| 38 | 
            +
                  JPEGWriter.new(self).write(io)
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            module Axon
         | 
| 2 | 
            +
              class NearestNeighborScaler < Scaler
         | 
| 3 | 
            +
                include NearestNeighborScaling
         | 
| 4 | 
            +
                include Image
         | 
| 5 | 
            +
                include Enumerable
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def each
         | 
| 8 | 
            +
                  dest_y = 0
         | 
| 9 | 
            +
                  sample_y = 0
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  @image.each_with_index do |scanline, orig_y|
         | 
| 12 | 
            +
                    while sample_y == orig_y
         | 
| 13 | 
            +
                      yield interpolate_scanline(scanline)
         | 
| 14 | 
            +
                      dest_y += 1
         | 
| 15 | 
            +
                      sample_y = (dest_y / @height_ratio).to_i
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
                
         | 
| 20 | 
            +
                private
         | 
| 21 | 
            +
                
         | 
| 22 | 
            +
                def _interpolate_scanline(scanline)
         | 
| 23 | 
            +
                  dest_sl = ''
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  scanline.size.times do |dest_x|
         | 
| 26 | 
            +
                    sample_x = (dest_x / @width_ratio).to_i
         | 
| 27 | 
            +
                    dest_sl << scanline[sample_x * components, components]
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  return dest_sl      
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              module Image
         | 
| 35 | 
            +
                def scale_nearest_neighbor(*args)
         | 
| 36 | 
            +
                  NearestNeighborScaler.new(self, *args)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require 'stringio'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Axon
         | 
| 4 | 
            +
              class PNGWriter
         | 
| 5 | 
            +
                include PNGNativeWriter
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                attr_reader :image
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(image)
         | 
| 10 | 
            +
                  @image = image
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def write(io)
         | 
| 14 | 
            +
                  png_native_write(io)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def data
         | 
| 18 | 
            +
                  s = StringIO.new
         | 
| 19 | 
            +
                  s.set_encoding 'BINARY' if s.respond_to?(:set_encoding)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  write(s)
         | 
| 22 | 
            +
                  s.string
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              module Image
         | 
| 27 | 
            +
                def to_png
         | 
| 28 | 
            +
                  PNGWriter.new self
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def write_png(io)
         | 
| 32 | 
            +
                  PNGWriter.new(self).write(io)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/axon/scaler.rb
    ADDED
    
    | @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            module Axon
         | 
| 2 | 
            +
              class Scaler
         | 
| 3 | 
            +
                attr_reader :width, :height
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(image, *args)
         | 
| 6 | 
            +
                  @image = image
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  if args.size == 1
         | 
| 9 | 
            +
                    init_ratio(args[0])
         | 
| 10 | 
            +
                  elsif args.size == 2
         | 
| 11 | 
            +
                    init_dims(args[0], args[1])
         | 
| 12 | 
            +
                  else
         | 
| 13 | 
            +
                    raise ArgumentError, "Must give one or two arguments"
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def components
         | 
| 18 | 
            +
                  @image.components
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def color_model
         | 
| 22 | 
            +
                  @image.color_model
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                private
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def init_ratio(ratio)
         | 
| 28 | 
            +
                  @width_ratio = @height_ratio = ratio.to_f
         | 
| 29 | 
            +
                  @width = (@image.width * ratio).floor
         | 
| 30 | 
            +
                  @height = (@image.height * ratio).floor
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def init_dims(width, height)
         | 
| 34 | 
            +
                  @width_ratio = width.to_f / @image.width
         | 
| 35 | 
            +
                  @width = width
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  @height_ratio = height.to_f / @image.height
         | 
| 38 | 
            +
                  @height = height
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
    
        data/lib/axon/solid.rb
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module Axon
         | 
| 2 | 
            +
              class Solid
         | 
| 3 | 
            +
                include Image
         | 
| 4 | 
            +
                include Enumerable
         | 
| 5 | 
            +
                attr_reader :width, :height, :color_model, :components
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(width, height, color=nil, color_model=nil)
         | 
| 8 | 
            +
                  @width, @height = width, height
         | 
| 9 | 
            +
                  @color = color || "\x00\x00\x00"
         | 
| 10 | 
            +
                  @color_model = color_model || :RGB
         | 
| 11 | 
            +
                  @components = @color.size
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def each
         | 
| 15 | 
            +
                  sl = @color * width
         | 
| 16 | 
            +
                  height.times { yield sl }
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              module Image
         | 
| 21 | 
            +
                # empty
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
    
        data/lib/axon.rb
    ADDED
    
    | @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            require 'axon/axon'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'axon/solid'
         | 
| 4 | 
            +
            require 'axon/cropper'
         | 
| 5 | 
            +
            require 'axon/scaler'
         | 
| 6 | 
            +
            require 'axon/nearest_neighbor_scaler'
         | 
| 7 | 
            +
            require 'axon/bilinear_scaler'
         | 
| 8 | 
            +
            require 'axon/fit'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            require 'axon/jpeg_writer'
         | 
| 11 | 
            +
            require 'axon/png_writer'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            module Axon
         | 
| 14 | 
            +
              VERSION = '0.0.1'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def self.JPEG(thing, markers=nil)
         | 
| 17 | 
            +
                if thing.respond_to? :read
         | 
| 18 | 
            +
                  io = thing
         | 
| 19 | 
            +
                  rewind_after_scanlines = false
         | 
| 20 | 
            +
                elsif thing[0, 1] == "\xFF"
         | 
| 21 | 
            +
                  io = StringIO.new(thing)
         | 
| 22 | 
            +
                  rewind_after_scanlines = true
         | 
| 23 | 
            +
                else
         | 
| 24 | 
            +
                  io = File.open(thing)
         | 
| 25 | 
            +
                  rewind_after_scanlines = true
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                JPEGReader.new(io, markers, rewind_after_scanlines)
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              def self.PNG(thing)
         | 
| 32 | 
            +
                if thing.respond_to? :read
         | 
| 33 | 
            +
                  io = thing
         | 
| 34 | 
            +
                  rewind_after_scanlines = false
         | 
| 35 | 
            +
                elsif thing[0, 1] == "\x89"
         | 
| 36 | 
            +
                  io = StringIO.new(thing)
         | 
| 37 | 
            +
                  rewind_after_scanlines = true
         | 
| 38 | 
            +
                else
         | 
| 39 | 
            +
                  io = File.open(thing)
         | 
| 40 | 
            +
                  rewind_after_scanlines = true
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                PNGReader.new(io)
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Axon
         | 
| 4 | 
            +
              class TestReader < AxonTestCase
         | 
| 5 | 
            +
                def setup
         | 
| 6 | 
            +
                  data = Solid.new(100, 200, "\x0A\x14\x69").to_jpeg.data
         | 
| 7 | 
            +
                  @io_in = StringIO.new(data)
         | 
| 8 | 
            +
                  @io_out = StringIO.new
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def test_short_chained_example
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  # Short, chained example. Reads a JPEG from io_in and writes scaled png to
         | 
| 14 | 
            +
                  # io_out.
         | 
| 15 | 
            +
                  Axon.JPEG(@io_in).fit(100, 100).write_png(@io_out)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def test_longer_example
         | 
| 19 | 
            +
                  # Even longer example, reads the JPEG header, looks at properties and header
         | 
| 20 | 
            +
                  # values, sets decompression options, scales the image, sets compression
         | 
| 21 | 
            +
                  # options, and writes a JPEG.
         | 
| 22 | 
            +
                  image = Axon.JPEG(@io_in, [:APP2])
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  puts image.width
         | 
| 25 | 
            +
                  puts image.height
         | 
| 26 | 
            +
                  puts image[:APP2]
         | 
| 27 | 
            +
                  image.scale_denom = 4
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  jpeg = image.fit(100, 100).to_jpeg
         | 
| 30 | 
            +
                  jpeg.quality = 88
         | 
| 31 | 
            +
                  jpeg.write(@io_out)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
    
        data/test/helper.rb
    ADDED
    
    | @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            require 'minitest/autorun'
         | 
| 2 | 
            +
            require 'axon'
         | 
| 3 | 
            +
            require 'stringio'
         | 
| 4 | 
            +
            require 'reader_tests'
         | 
| 5 | 
            +
            require 'writer_tests'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Axon
         | 
| 8 | 
            +
              class AxonTestCase < MiniTest::Unit::TestCase
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
                # Generate a solid velvet JPEG
         | 
| 11 | 
            +
                def setup
         | 
| 12 | 
            +
                  @velvet = "\x0A\x14\x69"
         | 
| 13 | 
            +
                  @image = Solid.new 10, 15, @velvet
         | 
| 14 | 
            +
                  @io_out = StringIO.new
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,115 @@ | |
| 1 | 
            +
            module Axon
         | 
| 2 | 
            +
              module ReaderTests
         | 
| 3 | 
            +
                def test_read_image_from_io
         | 
| 4 | 
            +
                  assert_equal 10, @reader.width
         | 
| 5 | 
            +
                  assert_equal 15, @reader.height
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                def test_read_image_from_string
         | 
| 9 | 
            +
                  assert_equal 10, @reader.width
         | 
| 10 | 
            +
                  assert_equal 15, @reader.height
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
                
         | 
| 13 | 
            +
                def test_num_components
         | 
| 14 | 
            +
                  assert_equal 3, @reader.components
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                def test_color_model
         | 
| 18 | 
            +
                  assert_equal :RGB, @reader.color_model
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                class DoubleIO < StringIO
         | 
| 22 | 
            +
                  def read(*args)
         | 
| 23 | 
            +
                    s = super
         | 
| 24 | 
            +
                    s[0..20] * 100
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
                def test_io_returns_too_much
         | 
| 29 | 
            +
                  f = DoubleIO.new @data
         | 
| 30 | 
            +
                  assert_raises(RuntimeError) { @readerclass.new f }
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
                class NilIO
         | 
| 34 | 
            +
                  def read(*args); end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                def test_io_returns_nil
         | 
| 38 | 
            +
                  assert_raises(RuntimeError) { @readerclass.new NilIO.new }
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                class RaiseIO
         | 
| 42 | 
            +
                  def read(*args)
         | 
| 43 | 
            +
                    raise 'heck'
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
                
         | 
| 47 | 
            +
                def test_io_raises_exception
         | 
| 48 | 
            +
                  assert_raises(RuntimeError) { @readerclass.new RaiseIO.new }
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                class EmptyStringIO
         | 
| 52 | 
            +
                  def read(*args)
         | 
| 53 | 
            +
                    ""
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def test_empty_string_io
         | 
| 58 | 
            +
                  assert_raises(RuntimeError) { @readerclass.new EmptyStringIO.new }
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
                
         | 
| 61 | 
            +
                class ThatsNotAStringIO
         | 
| 62 | 
            +
                  def read(*args)
         | 
| 63 | 
            +
                    :this_should_be_a_string
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
                
         | 
| 67 | 
            +
                def test_not_a_string_io
         | 
| 68 | 
            +
                  assert_raises(TypeError) { @readerclass.new ThatsNotAStringIO.new }
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
                
         | 
| 71 | 
            +
                def test_each
         | 
| 72 | 
            +
                  size = @reader.width * @reader.components
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  @reader.each do |scan_line|
         | 
| 75 | 
            +
                    assert_equal size, scan_line.size
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
                
         | 
| 79 | 
            +
                def test_no_shenanigans_during_each
         | 
| 80 | 
            +
                  @reader.each do |sl|
         | 
| 81 | 
            +
                    assert_raises(RuntimeError) { @reader.each{} }
         | 
| 82 | 
            +
                    break
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
                
         | 
| 86 | 
            +
                class OneExceptionIO < StringIO
         | 
| 87 | 
            +
                  def initialize(*args)
         | 
| 88 | 
            +
                    @raised_exception = false
         | 
| 89 | 
            +
                    super
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                  
         | 
| 92 | 
            +
                  def read(*args)
         | 
| 93 | 
            +
                    unless @raised_exception
         | 
| 94 | 
            +
                      @raised_exception = true
         | 
| 95 | 
            +
                      raise 'heck'
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
                    super
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
                
         | 
| 101 | 
            +
                def test_recovers_from_initial_io_exception
         | 
| 102 | 
            +
                  io = OneExceptionIO.new @data
         | 
| 103 | 
            +
                  r = @readerclass.allocate
         | 
| 104 | 
            +
                  assert_raises(RuntimeError) { r.send(:initialize, io) }
         | 
| 105 | 
            +
                  r.send(:initialize, io)
         | 
| 106 | 
            +
                  r.each{ }
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def test_multiple_each_calls
         | 
| 110 | 
            +
                  @reader.each{ }
         | 
| 111 | 
            +
                  @io_in.rewind
         | 
| 112 | 
            +
                  @reader.each{ }
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
              end
         | 
| 115 | 
            +
            end
         | 
| @@ -0,0 +1,71 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Axon
         | 
| 4 | 
            +
              class TestStressCases < AxonTestCase
         | 
| 5 | 
            +
                def setup
         | 
| 6 | 
            +
                  @velvet = "\x0A\x14\x69"
         | 
| 7 | 
            +
                  @image = Generator::Solid.new 1000, 1500, @velvet
         | 
| 8 | 
            +
                  @jpeg_data = @image.to_jpeg.data
         | 
| 9 | 
            +
                  @readerclass = JPEGReader
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                class RandomRaiseIO
         | 
| 13 | 
            +
                  def initialize(source)
         | 
| 14 | 
            +
                    @io = StringIO.new source
         | 
| 15 | 
            +
                    @die_at = rand(source.size)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def read
         | 
| 19 | 
            +
                    if @die_at < 2
         | 
| 20 | 
            +
                      raise 'random death!'
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    read_size = rand @die_at
         | 
| 24 | 
            +
                    @die_at -= read_size
         | 
| 25 | 
            +
                    @io.read(read_size)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def test_io_raises_after_random_sized_reads
         | 
| 30 | 
            +
                  loop do
         | 
| 31 | 
            +
                    f = RandomRaiseIO.new(@jpeg_data)
         | 
| 32 | 
            +
                    r = @readerclass.new(f) rescue puts("new failed")
         | 
| 33 | 
            +
                    r.each{} rescue puts('each_sl failed')
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                class CustomIO < StringIO
         | 
| 38 | 
            +
                  def initialize(str, sequence)
         | 
| 39 | 
            +
                    super str
         | 
| 40 | 
            +
                    @sequence = sequence
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def read
         | 
| 44 | 
            +
                    if !@sequence || @sequence.empty?
         | 
| 45 | 
            +
                      raise "random death!"
         | 
| 46 | 
            +
                      @sequence = nil
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    super(@sequence.shift)
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def test_custom_io_error
         | 
| 53 | 
            +
                  loop do
         | 
| 54 | 
            +
                    f = CustomIO.new(@jpeg_data, [114, 64, 8, 200, 200, 30, 350])
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    r = @readerclass.new(f)
         | 
| 57 | 
            +
                    r.each{  } rescue nil
         | 
| 58 | 
            +
                    f = CustomIO.new(@jpeg_data, [101, 0])
         | 
| 59 | 
            +
                    r = @readerclass.new(f) rescue nil
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def test_io_returns_nil_after_random_sized_reads
         | 
| 64 | 
            +
                  loop do
         | 
| 65 | 
            +
                    f = SometimesNilIO.new(@data)
         | 
| 66 | 
            +
                    r = @readerclass.new(f)
         | 
| 67 | 
            +
                    r.each{}
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
            end
         | 
    
        data/test/test_exif.rb
    ADDED
    
    | @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Axon
         | 
| 4 | 
            +
              class TestExif < AxonTestCase
         | 
| 5 | 
            +
                def test_exif_roundtrip
         | 
| 6 | 
            +
                  random_data = ""
         | 
| 7 | 
            +
                  (100).times do
         | 
| 8 | 
            +
                    random_data << [rand].pack('d') # 800 bytes random data
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  writer = @image.to_jpeg
         | 
| 12 | 
            +
                  writer.exif = random_data
         | 
| 13 | 
            +
                  
         | 
| 14 | 
            +
                  image_with_exif = Axon.JPEG(writer.data)
         | 
| 15 | 
            +
                  assert_equal random_data, image_with_exif.exif      
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                def test_exif_with_icc_roundtrip
         | 
| 19 | 
            +
                  random_icc_data = ""
         | 
| 20 | 
            +
                  (2**16).times do                  # a little larger than one jpeg segment
         | 
| 21 | 
            +
                    random_icc_data << [rand].pack('d') # 8 bytes random data
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  random_exif_data = ""
         | 
| 25 | 
            +
                  (100).times do
         | 
| 26 | 
            +
                    random_exif_data << [rand].pack('d') # 800 bytes random data
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                  
         | 
| 29 | 
            +
                  writer = @image.to_jpeg
         | 
| 30 | 
            +
                  writer.icc_profile = random_icc_data
         | 
| 31 | 
            +
                  writer.exif = random_exif_data
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  image_with_exif_and_icc = Axon.JPEG(writer.data)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  assert_equal random_exif_data, image_with_exif_and_icc.exif
         | 
| 36 | 
            +
                  assert_equal random_icc_data, image_with_exif_and_icc.icc_profile
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
    
        data/test/test_icc.rb
    ADDED
    
    | @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Axon
         | 
| 4 | 
            +
              class TestICC < AxonTestCase
         | 
| 5 | 
            +
                def test_large_icc_roundtrip
         | 
| 6 | 
            +
                  random_data = ""
         | 
| 7 | 
            +
                  (2**16).times do                  # a little larger than one jpeg segment
         | 
| 8 | 
            +
                    random_data << [rand].pack('d') # 8 bytes random data
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  writer = @image.to_jpeg
         | 
| 12 | 
            +
                  writer.icc_profile = random_data
         | 
| 13 | 
            +
                  
         | 
| 14 | 
            +
                  image_with_icc = Axon.JPEG(writer.data)
         | 
| 15 | 
            +
                  assert_equal random_data, image_with_icc.icc_profile
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         |