Module: ZSteg::Checker::ScanlineChecker

Defined in:
lib/zsteg/checker/scanline_checker.rb

Class Method Summary collapse

Class Method Details

.check_image(image, params = {}) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
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
# File 'lib/zsteg/checker/scanline_checker.rb', line 6

def check_image image, params={}
  # TODO: interlaced images
  sl = image.scanlines.first
  significant_bits = sl.width*sl.bpp
  total_bits = sl.size*8
  # 1 byte for PNG scanline filter mode
  # XXX maybe move this into ZPNG::ScanLine#data_size ?
  total_bits -= 8 if image.format == :png
  return if total_bits == significant_bits

  #puts "[nbits] tb=#{total_bits}, sb=#{significant_bits}, nbits=#{total_bits-significant_bits}"
  nbits = total_bits-significant_bits
  raise "WTF" if nbits<0      # significant size greatar than total size?!

  data = ''
  scanlines = image.scanlines
  # DO NOT use 'reverse!' here - it will affect original image too
  scanlines = scanlines.reverse if image.format == :bmp
  if nbits%8 == 0
    # whole bytes
    nbytes = nbits/8
    scanlines.each do |sl|
      data << sl.decoded_bytes[-nbytes,nbytes]
    end
  else
    # extract a number of bits from each scanline
    nbytes = (nbits/8.0).ceil # number of whole bytes, rounded up
    mask = 2**nbits-1
    a = []
    scanlines.each do |sl|
      bytes = sl.decoded_bytes[-nbytes,nbytes]
      value = 0
      # convert 1+ bytes into one big integer
      bytes.each_byte{ |b| value = (value<<8) + b }

      # remove unwanted bits
      value &= mask

      # fix[n] -> 0, 1
      # Bit Reference - Returns the nth bit in the binary representation of fix
      # http://www.ruby-doc.org/core-1.9.3/Fixnum.html#method-i-5B-5D
      #
      # also "<<" + "reverse!" is 30% faster than "unshift"
      nbits.times{ |i| a << value[i] }
      a.reverse!

      while a.size >= 8
        byte = 0
        if params[:bit_order] == :msb
          8.times{ |i| byte |= (a.shift<<i)}
        else
          8.times{ |i| byte |= (a.shift<<(7-i))}
        end
        data << byte.chr
#                if data.size >= @limit
#                  print "[limit #@limit]".gray if @verbose > 1
#                  break
#                end
      end
    end
  end
  return if data =~ /\A\x00+\Z/ # nothing special, only zero bytes

  # something found
  data
end