class Capybara::Screenshot::Diff::Drivers::ChunkyPNGDriver
Attributes
new_file_name[R]
old_file_name[R]
Public Class Methods
new(new_file_name, old_file_name = nil, **options)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 16 def initialize(new_file_name, old_file_name = nil, **options) @new_file_name = new_file_name @old_file_name = old_file_name || "#{new_file_name}~" @color_distance_limit = options[:color_distance_limit] @shift_distance_limit = options[:shift_distance_limit] @skip_area = options[:skip_area] reset end
Public Instance Methods
add_black_box(image, _region)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 44 def add_black_box(image, _region) image end
adds_error_details_to(log)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 107 def adds_error_details_to(log) max_color_distance = self.max_color_distance.ceil(1) max_shift_distance = self.max_shift_distance log[:max_color_distance] = max_color_distance log.merge!(max_shift_distance: max_shift_distance) if max_shift_distance end
calculate_max_color_distance(new_image, old_image)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 139 def calculate_max_color_distance(new_image, old_image) pixel_pairs = old_image.pixels.zip(new_image.pixels) @max_color_distance = pixel_pairs.inject(0) { |max, (p1, p2)| next max unless p1 && p2 d = ChunkyPNG::Color.euclidean_distance_rgba(p1, p2) [max, d].max } end
calculate_max_shift_limit(new_img, old_img)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 149 def calculate_max_shift_limit(new_img, old_img) (0...new_img.width).each do |x| (0...new_img.height).each do |y| shift_distance = shift_distance_at(new_img, old_img, x, y, color_distance_limit: @color_distance_limit) if shift_distance && (@max_shift_distance.nil? || shift_distance > @max_shift_distance) @max_shift_distance = shift_distance return if @max_shift_distance == Float::INFINITY # rubocop: disable Lint/NonLocalExitFromIterator end end end end
calculate_metrics()
click to toggle source
private
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 125 def calculate_metrics old_file, new_file = load_image_files(@old_file_name, @new_file_name) if old_file == new_file @max_color_distance = 0 @max_shift_distance = 0 return end old_image, new_image = _load_images(old_file, new_file) calculate_max_color_distance(new_image, old_image) calculate_max_shift_limit(new_image, old_image) end
crop(dimensions, i)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 115 def crop(dimensions, i) i.crop(0, 0, *dimensions) end
difference_level(_diff_mask, old_img, region)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 48 def difference_level(_diff_mask, old_img, region) size(region).to_f / image_area_size(old_img) end
dimension_changed?(old_image, new_image)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 176 def dimension_changed?(old_image, new_image) return unless old_image.dimension != new_image.dimension change_msg = [old_image, new_image].map { |i| "#{i.width}x#{i.height}" }.join(" => ") warn "Image size has changed for #{@new_file_name}: #{change_msg}" true end
draw_rectangles(images, (left, top, right, bottom), (r, g, b))
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 184 def draw_rectangles(images, (left, top, right, bottom), (r, g, b)) images.map do |image| new_img = image.dup new_img.rect(left - 1, top - 1, right + 1, bottom + 1, ChunkyPNG::Color.rgb(r, g, b)) new_img end end
filter_image_with_median(_image)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 40 def filter_image_with_median(_image) raise NotImplementedError end
find_difference_region(new_image, old_image, color_distance_limit, shift_distance_limit, area_size_limit, fast_fail: false)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 66 def find_difference_region(new_image, old_image, color_distance_limit, shift_distance_limit, area_size_limit, fast_fail: false) return nil, nil if new_image.pixels == old_image.pixels if fast_fail && !(color_distance_limit || shift_distance_limit || area_size_limit) return [0, 0, width_for(new_image), height_for(new_image)], nil end region = find_top(old_image, new_image) region = if region.nil? || region[1].nil? nil else find_diff_rectangle(old_image, new_image, region) end [region, nil] end
from_file(filename)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 119 def from_file(filename) ChunkyPNG::Image.from_file(filename) end
height_for(image)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 83 def height_for(image) image.height end
image_area_size(old_img)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 52 def image_area_size(old_img) width_for(old_img) * height_for(old_img) end
load_image_files(old_file_name, file_name)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 170 def load_image_files(old_file_name, file_name) old_file = File.binread(old_file_name) new_file = File.binread(file_name) [old_file, new_file] end
load_images(old_file_name, new_file_name)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 34 def load_images(old_file_name, new_file_name) old_bytes, new_bytes = load_image_files(old_file_name, new_file_name) _load_images(old_bytes, new_bytes) end
max_color_distance()
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 97 def max_color_distance calculate_metrics unless @max_color_distance @max_color_distance end
max_shift_distance()
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 102 def max_shift_distance calculate_metrics unless @max_shift_distance || !@shift_distance_limit @max_shift_distance end
reset()
click to toggle source
Resets the calculated data about the comparison with regard to the “new_image”. Data about the original image is kept.
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 29 def reset @max_color_distance = @color_distance_limit ? 0 : nil @max_shift_distance = @shift_distance_limit ? 0 : nil end
resize_image_to(image, new_width, new_height)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 166 def resize_image_to(image, new_width, new_height) image.resample_bilinear(new_width, new_height) end
save_image_to(image, filename)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 162 def save_image_to(image, filename) image.save(filename) end
shift_distance_different?()
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 61 def shift_distance_different? # Stub true end
shift_distance_equal?()
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 56 def shift_distance_equal? # Stub false end
size(region)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 91 def size(region) return 0 unless region (region[2] - region[0] + 1) * (region[3] - region[1] + 1) end
width_for(image)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 87 def width_for(image) image.width end
Private Instance Methods
_load_images(old_file, new_file)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 348 def _load_images(old_file, new_file) [ChunkyPNG::Image.from_blob(old_file), ChunkyPNG::Image.from_blob(new_file)] end
color_distance_at(new_img, old_img, x, y, shift_distance_limit:)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 267 def color_distance_at(new_img, old_img, x, y, shift_distance_limit:) org_color = old_img[x, y] if shift_distance_limit start_x = [0, x - shift_distance_limit].max end_x = [x + shift_distance_limit, new_img.width - 1].min xs = (start_x..end_x).to_a start_y = [0, y - shift_distance_limit].max end_y = [y + shift_distance_limit, new_img.height - 1].min ys = (start_y..end_y).to_a new_pixels = xs.product(ys) distances = new_pixels.map { |dx, dy| new_color = new_img[dx, dy] ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color) } distances.min else ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_img[x, y]) end end
color_matches(new_img, org_color, x, y, color_distance_limit)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 340 def color_matches(new_img, org_color, x, y, color_distance_limit) new_color = new_img[x, y] return new_color == org_color unless color_distance_limit color_distance = ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color) color_distance <= color_distance_limit end
find_bottom(old_img, new_img, left, right, bottom)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 234 def find_bottom(old_img, new_img, left, right, bottom) if bottom (old_img.height - 1).step(bottom + 1, -1).find do |y| (left..right).find do |x| bottom = y unless same_color?(old_img, new_img, x, y) end end end bottom end
find_diff_rectangle(org_img, new_img, region)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 194 def find_diff_rectangle(org_img, new_img, region) left, top, right, bottom = find_left_right_and_top(org_img, new_img, region) bottom = find_bottom(org_img, new_img, left, right, bottom) [left, top, right, bottom] end
find_left_right_and_top(old_img, new_img, region)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 209 def find_left_right_and_top(old_img, new_img, region) left = region[0] || old_img.width - 1 top = region[1] right = region[2] || 0 bottom = region[3] old_img.height.times do |y| (0...left).find do |x| next if same_color?(old_img, new_img, x, y) top ||= y bottom = y left = x right = x if x > right x end (old_img.width - 1).step(right + 1, -1).find do |x| unless same_color?(old_img, new_img, x, y) bottom = y right = x end end end [left, top, right, bottom] end
find_top(old_img, new_img)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 200 def find_top(old_img, new_img) old_img.height.times do |y| old_img.width.times do |x| return [x, y, x, y] unless same_color?(old_img, new_img, x, y) end end nil end
same_color?(old_img, new_img, x, y)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 245 def same_color?(old_img, new_img, x, y) @skip_area&.each do |skip_start_x, skip_start_y, skip_end_x, skip_end_y| return true if skip_start_x <= x && x <= skip_end_x && skip_start_y <= y && y <= skip_end_y end color_distance = color_distance_at(new_img, old_img, x, y, shift_distance_limit: @shift_distance_limit) if !@max_color_distance || color_distance > @max_color_distance @max_color_distance = color_distance end color_matches = color_distance == 0 || (@color_distance_limit && @color_distance_limit > 0 && color_distance <= @color_distance_limit) return color_matches if !@shift_distance_limit || @max_shift_distance == Float::INFINITY shift_distance = (color_matches && 0) || shift_distance_at(new_img, old_img, x, y, color_distance_limit: @color_distance_limit) if shift_distance && (@max_shift_distance.nil? || shift_distance > @max_shift_distance) @max_shift_distance = shift_distance end color_matches end
shift_distance_at(new_img, old_img, x, y, color_distance_limit:)
click to toggle source
# File lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb, line 287 def shift_distance_at(new_img, old_img, x, y, color_distance_limit:) org_color = old_img[x, y] shift_distance = 0 loop do bounds_breached = 0 top_row = y - shift_distance if top_row >= 0 # top ([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx| if color_matches(new_img, org_color, dx, top_row, color_distance_limit) return shift_distance end end else bounds_breached += 1 end if shift_distance > 0 if (x - shift_distance) >= 0 # left ([0, top_row + 1].max..[y + shift_distance, new_img.height - 2].min) .each do |dy| if color_matches(new_img, org_color, x - shift_distance, dy, color_distance_limit) return shift_distance end end else bounds_breached += 1 end if (y + shift_distance) < new_img.height # bottom ([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx| if color_matches(new_img, org_color, dx, y + shift_distance, color_distance_limit) return shift_distance end end else bounds_breached += 1 end if (x + shift_distance) < new_img.width # right ([0, top_row + 1].max..[y + shift_distance, new_img.height - 2].min) .each do |dy| if color_matches(new_img, org_color, x + shift_distance, dy, color_distance_limit) return shift_distance end end else bounds_breached += 1 end end break if bounds_breached == 4 shift_distance += 1 end Float::INFINITY end