module ZSteg::Checker::ScanlineChecker

Public Class Methods

check_image(image, params={}) click to toggle source
# 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