Class: Applitools::Utils::ImageDeltaCompressor

Inherits:
Object
  • Object
show all
Defined in:
lib/eyes_selenium/utils/image_delta_compressor.rb

Defined Under Namespace

Classes: CompareAndCopyBlockChannelDataResult, Dimension

Class Method Summary collapse

Class Method Details

.compress_by_raw_blocks(target, target_encoded, source, block_size = 10) ⇒ Object

Compresses the target image based on the source image.

target

ChunkyPNG::Canvas The image to compress based on the source image.

target_encoded

Array The uncompressed image as binary string.

source

ChunkyPNG::Canvas The source image used as a base for compressing the target image.

block_size

Integer The width/height of each block.

Returns String The binary result (either the compressed image, or the uncompressed image if the compression is greater in length).



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/eyes_selenium/utils/image_delta_compressor.rb', line 19

def self.compress_by_raw_blocks(target, target_encoded, source, block_size = 10)
  # If we can't compress for any reason, return the target image as is.
  if source.nil? || (source.height != target.height) || (source.width != target.width)
    # Returning a COPY of the target binary string
    return String.new(target_encoded) 
  end

  # Preparing the variables we need
  target_pixels = target.to_rgb_stream.unpack('C*')
  source_pixels = source.to_rgb_stream.unpack('C*')
  image_size = Dimension.new(target.width, target.height)
  block_columns_count = (target.width / block_size) + ((target.width % block_size) == 0 ? 0 : 1)
  block_rows_count = (target.height / block_size) + ((target.height % block_size) == 0 ? 0 : 1)

  # IMPORTANT: The "-Zlib::MAX_WBITS" tells ZLib to create raw deflate compression, without the
  # "Zlib headers" (this isn't documented in the Zlib page, I found this in some internet forum).
  compressor = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS)

  compression_result = ''

  # Writing the data header
  compression_result += @@PREAMBLE.encode("UTF-8")
  compression_result += [@@FORMAT_RAW_BLOCKS].pack("C")
  compression_result += [0].pack("S>") #Source id, Big Endian
  compression_result += [block_size].pack("S>") #Big Endian


  # We perform the compression for each channel
  3.times do |channel|
    block_number = 0
    block_rows_count.times do |block_row|
      block_columns_count.times do |block_column|
        actual_channel_index = 2 - channel # Since the image bytes are BGR and the server expects RGB...
        compare_result = compare_and_copy_block_channel_data(source_pixels, target_pixels, image_size, 3, block_size,
                                                             block_column, block_row, actual_channel_index)

        if !compare_result.identical
          channel_bytes = compare_result.channel_bytes
          string_to_compress = [channel].pack('C')
          string_to_compress += [block_number].pack('L>')
          string_to_compress += channel_bytes.pack('C*')

          compression_result += compressor.deflate(string_to_compress)

          # If the compressed data so far is greater than the uncompressed
          # representation of the target, just return the target.
          if compression_result.length > target_encoded.length
     compressor.finish
            compressor.close
            # Returning a COPY of the target bytes
            return String.new(target_encoded)
          end
        end
        block_number += 1
      end
    end
  end
  # Compress and flush any remaining uncompressed data in the input buffer.
  compression_result += compressor.finish
  compressor.close
  # Returning the compressed result as a byte array
  return compression_result
end