Module: ZSteg::Extractor::ColorExtractor

Included in:
ZSteg::Extractor
Defined in:
lib/zsteg/extractor/color_extractor.rb

Overview

ColorExtractor extracts bits from each pixel’s color

Instance Method Summary collapse

Instance Method Details

#color_extract(params = {}) ⇒ Object



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
72
73
74
75
76
# File 'lib/zsteg/extractor/color_extractor.rb', line 7

def color_extract params = {}
  channels = Array(params[:channels])
  pixel_align = params[:pixel_align]

  ch_masks = []
  case channels.first.size
  when 1
    # ['r', 'g', 'b']
    channels.each{ |c| ch_masks << [c[0], bit_indexes(params[:bits])] }
  when 2
    # ['r3', 'g2', 'b3']
    channels.each{ |c| ch_masks << [c[0], bit_indexes(c[1].to_i)] }
  else
    raise "invalid channels: #{channels.inspect}" if channels.size != 1
    t = channels.first
    if t =~ /\A[rgba]+\Z/
      return color_extract(params.merge(:channels => t.split('')))
    end
    raise "invalid channels: #{channels.inspect}"
  end

  # total number of bits = sum of all channels bits
  nbits = ch_masks.map{ |x| x[1].size }.inject(&:+)

  if params[:prime]
    pregenerate_primes(
      :max   => @image.width * @image.height,
      :count => (@limit*8.0/nbits/channels.size).ceil
    )
  end

  data = ''.force_encoding('binary')
  a = [0]*params[:shift].to_i        # prepend :shift zero bits
  catch :limit do
    coord_iterator(params) do |x,y|
      color = @image[x,y]

      ch_masks.each do |c,bidxs|
        bidxs = bidxs[a.size-8..] if pixel_align && a.size + bidxs.size > 8
        value = color.send(c)
        bidxs.each do |bidx|
          a << value[bidx]
        end
      end

      while a.size >= 8
        byte = 0
        # a0 = a.dup
        if params[:bit_order] == :msb
          8.times{ |i| byte |= (a.shift<<i)}
        else
          8.times{ |i| byte |= (a.shift<<(7-i))}
        end
        # printf "[d] %-10s -> %-10s : %s %02x %08b  x=%d y=%d\n", a0.join, a.join, byte.chr.inspect, byte, byte, x, y
        data << byte.chr
        if data.size >= @limit
          print "[limit #@limit]".gray if @verbose > 1
          throw :limit
        end
        a.clear if pixel_align && a.size < 8
      end
    end
  end
  if params[:strip_tail_zeroes] != false && data[-1,1] == "\x00"
    oldsz = data.size
    data.sub!(/\x00+\Z/,'')
    print "[zerotail #{oldsz-data.size}]".gray if @verbose > 1
  end
  data
end

#coord_iterator(params) ⇒ Object

‘xy’: x=0,y=0; x=1,y=0; x=2,y=0; … ‘yx’: x=0,y=0; x=0,y=1; x=0,y=2; … … ‘xY’: x=0, y=MAX; x=1, y=MAX; x=2, y=MAX; … ‘XY’: x=MAX,y=MAX; x=MAX-1,y=MAX; x=MAX-2,y=MAX; …



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/zsteg/extractor/color_extractor.rb', line 83

def coord_iterator params
  type = params[:order]
  if type.nil? || type == 'auto'
    type = @image.format == :bmp ? 'xY' : 'xy'
  end
  raise "invalid iterator type #{type}" unless type =~ /\A(xy|yx)\Z/i

  x0,x1,xstep =
    if type.index('x')
      [0, @image.width-1, 1]
    else
      [@image.width-1, 0, -1]
    end

  y0,y1,ystep =
    if type.index('y')
      [0, @image.height-1, 1]
    else
      [@image.height-1, 0, -1]
    end

  # cannot join these lines from ByteExtractor and ColorExtractor into
  # one method for performance reason:
  #   it will require additional yield() for EACH BYTE iterated

  if type[0,1].downcase == 'x'
    # ROW iterator
    if params[:prime]
      idx = 0
      y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x|
        yield(x,y) if @primes.include?(idx)
        idx += 1
      }}
    else
      y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x| yield(x,y) }}
    end
  else
    # COLUMN iterator
    if params[:prime]
      idx = 0
      x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y|
        yield(x,y) if @primes.include?(idx)
        idx += 1
      }}
    else
      x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y| yield(x,y) }}
    end
  end
end