class Capybara::Screenshot::Diff::ImageCompare

Compare two images and determine if they are equal, different, or within some comparison range considering color values and difference area size.

Constants

DIFF_COLOR
SKIP_COLOR

Attributes

annotated_new_file_name[R]
annotated_old_file_name[R]
area_size_limit[R]
color_distance_limit[R]
driver[R]
driver_options[R]
new_file_name[R]
old_file_name[R]
shift_distance_limit[R]
skip_area[R]

Public Class Methods

new(new_file_name, old_file_name = nil, **driver_options) click to toggle source
Calls superclass method
# File lib/capybara/screenshot/diff/image_compare.rb, line 17
def initialize(new_file_name, old_file_name = nil, **driver_options)
  @new_file_name = new_file_name
  @old_file_name = old_file_name || "#{new_file_name}~"
  @annotated_old_file_name = "#{new_file_name.chomp(".png")}.committed.png"
  @annotated_new_file_name = "#{new_file_name.chomp(".png")}.latest.png"

  @driver_options = driver_options

  @color_distance_limit = driver_options[:color_distance_limit] || 0
  @area_size_limit = driver_options[:area_size_limit]
  @shift_distance_limit = driver_options[:shift_distance_limit]
  @dimensions = driver_options[:dimensions]
  @skip_area = driver_options[:skip_area]
  @tolerance = driver_options[:tolerance]
  @median_filter_window_size = driver_options[:median_filter_window_size]

  driver_klass = find_driver_class_for(@driver_options.fetch(:driver, :chunky_png))
  @driver = driver_klass.new(@new_file_name, @old_file_name, **@driver_options)

  super(@driver)
end

Public Instance Methods

annotate_and_save(images, region = difference_region) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 125
def annotate_and_save(images, region = difference_region)
  annotated_images = driver.draw_rectangles(images, region, DIFF_COLOR)
  @skip_area.to_a.flatten.each_slice(4) do |region|
    annotated_images = driver.draw_rectangles(annotated_images, region, SKIP_COLOR)
  end
  save(*annotated_images, @annotated_old_file_name, @annotated_new_file_name)
end
clean_tmp_files() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 115
def clean_tmp_files
  FileUtils.cp @old_file_name, @new_file_name if old_file_exists?
  File.delete(@old_file_name) if old_file_exists?
  File.delete(@annotated_old_file_name) if File.exist?(@annotated_old_file_name)
  File.delete(@annotated_new_file_name) if File.exist?(@annotated_new_file_name)
end
difference_region() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 158
def difference_region
  return nil unless @left || @top || @right || @bottom

  [@left, @top, @right, @bottom]
end
different?() click to toggle source

Compare the two images referenced by this object, and return `true` if they are different, and `false` if they are the same. Return `nil` if the old file does not exist or if the image dimensions do not match.

# File lib/capybara/screenshot/diff/image_compare.rb, line 79
def different?
  return nil unless old_file_exists?

  images = driver.load_images(@old_file_name, @new_file_name)

  old_image, new_image = preprocess_images(images, driver)

  if driver.dimension_changed?(old_image, new_image)
    save(new_image, old_image, @annotated_new_file_name, @annotated_old_file_name)

    self.difference_region = 0, 0, driver.width_for(old_image), driver.height_for(old_image)

    return true
  end

  region, meta = driver.find_difference_region(
    new_image,
    old_image,
    @color_distance_limit,
    @shift_distance_limit,
    @area_size_limit
  )
  self.difference_region = region

  return not_different if difference_region_empty?(old_image, region)
  return not_different if @area_size_limit && driver.size(region) <= @area_size_limit
  return not_different if @tolerance && @tolerance > driver.difference_level(meta, old_image, region)

  # TODO: Remove this or find similar solution for vips
  return not_different if @shift_distance_limit && !driver.shift_distance_different?

  annotate_and_save(images, region)

  true
end
error_message() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 147
def error_message
  result = {
    area_size: driver.size(difference_region),
    region: difference_region
  }

  driver.adds_error_details_to(result)

  ["(#{result.to_json})", new_file_name, annotated_old_file_name, annotated_new_file_name].join("\n")
end
old_file_exists?() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 138
def old_file_exists?
  @old_file_name && File.exist?(@old_file_name)
end
quick_equal?() click to toggle source

Compare the two image files and return `true` or `false` as quickly as possible. Return falsish if the old file does not exist or the image dimensions do not match.

# File lib/capybara/screenshot/diff/image_compare.rb, line 41
def quick_equal?
  return false unless old_file_exists?
  return true if new_file_size == old_file_size

  # old_bytes, new_bytes = load_image_files(@old_file_name, @new_file_name)
  # return true if old_bytes == new_bytes

  images = driver.load_images(@old_file_name, @new_file_name)
  old_image, new_image = preprocess_images(images, driver)

  return false if driver.dimension_changed?(old_image, new_image)

  region, meta = driver.find_difference_region(
    new_image,
    old_image,
    @color_distance_limit,
    @shift_distance_limit,
    @area_size_limit,
    fast_fail: true
  )

  self.difference_region = region

  return true if difference_region_empty?(new_image, region)

  return true if @area_size_limit && driver.size(region) <= @area_size_limit

  return true if @tolerance && @tolerance >= driver.difference_level(meta, old_image, region)

  # TODO: Remove this or find similar solution for vips
  return true if @shift_distance_limit && driver.shift_distance_equal?

  false
end
reset() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 142
def reset
  self.difference_region = nil
  driver.reset
end
save(old_img, new_img, annotated_old_file_name, annotated_new_file_name) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 133
def save(old_img, new_img, annotated_old_file_name, annotated_new_file_name)
  driver.save_image_to(old_img, annotated_old_file_name)
  driver.save_image_to(new_img, annotated_new_file_name)
end

Private Instance Methods

difference_region=(region) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 224
def difference_region=(region)
  @left, @top, @right, @bottom = region
end
difference_region_empty?(new_image, region) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 228
def difference_region_empty?(new_image, region)
  region.nil? ||
    (
      region[1] == height_for(new_image) &&
        region[0] == width_for(new_image) &&
        region[2].zero? &&
        region[3].zero?
    )
end
find_driver_class_for(driver) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 166
def find_driver_class_for(driver)
  driver = AVAILABLE_DRIVERS.first if driver == :auto

  LOADED_DRIVERS[driver] ||=
    case driver
    when :chunky_png
      require "capybara/screenshot/diff/drivers/chunky_png_driver"
      Drivers::ChunkyPNGDriver
    when :vips
      require "capybara/screenshot/diff/drivers/vips_driver"
      Drivers::VipsDriver
    else
      fail "Wrong adapter #{driver.inspect}. Available adapters: #{AVAILABLE_DRIVERS.inspect}"
    end
end
load_images(old_file_name, new_file_name, driver = self) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 195
def load_images(old_file_name, new_file_name, driver = self)
  [driver.from_file(old_file_name), driver.from_file(new_file_name)]
end
new_file_size() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 186
def new_file_size
  File.size(@new_file_name)
end
not_different() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 190
def not_different
  clean_tmp_files
  false
end
old_file_size() click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 182
def old_file_size
  @old_file_size ||= old_file_exists? && File.size(@old_file_name)
end
preprocess_image(image, driver = self) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 206
def preprocess_image(image, driver = self)
  result = image

  if @dimensions && driver.inscribed?(@dimensions, result)
    result = driver.crop(@dimensions, result)
  end

  if @median_filter_window_size
    result = driver.filter_image_with_median(image, @median_filter_window_size)
  end

  if @skip_area
    result = @skip_area.reduce(result) { |image, region| driver.add_black_box(image, region) }
  end

  result
end
preprocess_images(images, driver = self) click to toggle source
# File lib/capybara/screenshot/diff/image_compare.rb, line 199
def preprocess_images(images, driver = self)
  old_img = preprocess_image(images.first, driver)
  new_img = preprocess_image(images.last, driver)

  [old_img, new_img]
end