zsteg 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -7
- data/Gemfile.lock +2 -4
- data/README.md +72 -1
- data/README.md.tpl +23 -0
- data/Rakefile +5 -3
- data/TODO +5 -2
- data/VERSION +1 -1
- data/bin/zsteg-mask +7 -0
- data/lib/zsteg/checker/wbstego.rb +69 -14
- data/lib/zsteg/checker.rb +137 -34
- data/lib/zsteg/cli.rb +92 -35
- data/lib/zsteg/extractor/byte_extractor.rb +36 -21
- data/lib/zsteg/extractor/color_extractor.rb +68 -34
- data/lib/zsteg/extractor.rb +36 -1
- data/lib/zsteg/file_cmd.rb +64 -1
- data/lib/zsteg/mask_cli.rb +268 -0
- data/lib/zsteg/masker.rb +52 -0
- data/lib/zsteg/result.rb +27 -32
- data/lib/zsteg.rb +2 -0
- data/samples/hackquest/crypt.bmp +0 -0
- data/samples/hackquest/square.bmp +0 -0
- data/samples/wbstego/wbsteg_blowfish_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_cast128_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_enc_pass_pass.bmp +0 -0
- data/samples/wbstego/wbsteg_enc_pass_pass_even.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_1_even.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_foobar.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_pass.bmp +0 -0
- data/samples/wbstego/wbsteg_mixenc_pass_pass_even.bmp +0 -0
- data/samples/{wbsteg_noenc.bmp → wbstego/wbsteg_noenc.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc.png +0 -0
- data/samples/wbstego/wbsteg_noenc_.bmp +0 -0
- data/samples/{wbsteg_noenc_17.bmp → wbstego/wbsteg_noenc_17.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc__.bmp +0 -0
- data/samples/{wbsteg_noenc_even.bmp → wbstego/wbsteg_noenc_even.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc_even2.bmp +0 -0
- data/samples/{wbsteg_noenc_even_17.bmp → wbstego/wbsteg_noenc_even_17.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc_even_17_.bmp +0 -0
- data/samples/wbstego/wbsteg_noenc_ext_ABC.bmp +0 -0
- data/samples/wbstego/wbsteg_rijndael_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_rijndael_pass_pass.bmp +0 -0
- data/samples/wbstego/wbsteg_rijndael_pass_pass_even.bmp +0 -0
- data/samples/wbstego/wbsteg_twofish_pass_1.bmp +0 -0
- data/samples/wechall/5ZMGcCLxpcpsru03.g00000010.png +0 -0
- data/samples/wechall/5ZMGcCLxpcpsru03.png +0 -0
- data/samples/wechall/stegano1.bmp +0 -0
- data/spec/checker_spec.rb +47 -0
- data/spec/easybmp_spec.rb +9 -0
- data/spec/hackquest_spec.rb +18 -0
- data/spec/mask_spec.rb +14 -0
- data/spec/polictf2012_spec.rb +48 -0
- data/spec/prime_spec.rb +9 -0
- data/spec/r3g2b3_spec.rb +9 -0
- data/spec/spec_helper.rb +21 -4
- data/spec/wbstego_spec.rb +21 -3
- data/spec/wechall_spec.rb +26 -0
- data/tmp/.keep +0 -0
- data/zsteg.gemspec +121 -0
- metadata +47 -43
- data/samples/06_enc.png +0 -0
- data/samples/Code.png +0 -0
- data/samples/README +0 -4
- data/samples/camouflage-password.png +0 -0
- data/samples/camouflage.png +0 -0
- data/samples/cats.png +0 -0
- data/samples/flower.png +0 -0
- data/samples/flower_rgb1.png +0 -0
- data/samples/flower_rgb2.png +0 -0
- data/samples/flower_rgb3.png +0 -0
- data/samples/flower_rgb4.png +0 -0
- data/samples/flower_rgb5.png +0 -0
- data/samples/flower_rgb6.png +0 -0
- data/samples/montenach-enc.png +0 -0
- data/samples/ndh2k12_sp113.bmp.7z +0 -0
- data/samples/openstego_q2.png +0 -0
- data/samples/openstego_send.png +0 -0
- data/samples/stg300.png +0 -0
data/lib/zsteg/cli.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
require 'optparse'
|
2
|
-
require 'awesome_print'
|
3
2
|
|
4
3
|
module ZSteg
|
5
4
|
class CLI
|
6
5
|
DEFAULT_ACTIONS = %w'check'
|
7
|
-
DEFAULT_LIMIT = 256
|
8
|
-
DEFAULT_ORDER = 'auto'
|
9
6
|
|
10
7
|
def initialize argv = ARGV
|
11
8
|
@argv = argv
|
@@ -15,30 +12,35 @@ module ZSteg
|
|
15
12
|
@actions = []
|
16
13
|
@options = {
|
17
14
|
:verbose => 0,
|
18
|
-
:limit => DEFAULT_LIMIT,
|
19
|
-
:
|
20
|
-
:order => DEFAULT_ORDER
|
15
|
+
:limit => Checker::DEFAULT_LIMIT,
|
16
|
+
:order => Checker::DEFAULT_ORDER
|
21
17
|
}
|
22
18
|
optparser = OptionParser.new do |opts|
|
23
|
-
opts.banner = "Usage: zsteg [options] filename.png"
|
19
|
+
opts.banner = "Usage: zsteg [options] filename.png [param_string]"
|
24
20
|
opts.separator ""
|
25
21
|
|
26
|
-
opts.on("-c", "--channels X", /[rgba,]+/,
|
22
|
+
opts.on("-c", "--channels X", /[rgba,1-8]+/,
|
27
23
|
"channels (R/G/B/A) or any combination, comma separated",
|
28
|
-
"valid values: r,g,b,a,rg,
|
24
|
+
"valid values: r,g,b,a,rg,bgr,rgba,r3g2b3,..."
|
29
25
|
){ |x| @options[:channels] = x.split(',') }
|
30
26
|
|
31
27
|
opts.on("-l", "--limit N", Integer,
|
32
|
-
"limit bytes checked, 0 = no limit (default: #{
|
28
|
+
"limit bytes checked, 0 = no limit (default: #{@options[:limit]})"
|
33
29
|
){ |n| @options[:limit] = n }
|
34
30
|
|
35
|
-
opts.on("-b", "--bits N",
|
36
|
-
"
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
opts.on("-b", "--bits N", "number of bits, single int value or '1,3,5' or range '1-8'",
|
32
|
+
"advanced: specify individual bits like '00001110' or '0x88'"
|
33
|
+
) do |x|
|
34
|
+
a = []
|
35
|
+
x.split(',').each do |x1|
|
36
|
+
if x1['-']
|
37
|
+
t = x1.split('-')
|
38
|
+
a << Range.new(parse_bits(t[0]), parse_bits(t[1])).to_a
|
39
|
+
else
|
40
|
+
a << parse_bits(x1)
|
41
|
+
end
|
41
42
|
end
|
43
|
+
@options[:bits] = a.flatten.uniq
|
42
44
|
end
|
43
45
|
|
44
46
|
opts.on "--lsb", "least significant BIT comes first" do
|
@@ -48,8 +50,20 @@ module ZSteg
|
|
48
50
|
@options[:bit_order] = :msb
|
49
51
|
end
|
50
52
|
|
53
|
+
opts.on "-P", "--prime", "analyze/extract only prime bytes/pixels" do
|
54
|
+
@options[:prime] = true
|
55
|
+
end
|
56
|
+
# opts.on "--pixel-align", "pixel-align hidden data (EasyBMP)" do
|
57
|
+
# @options[:pixel_align] = true
|
58
|
+
# end
|
59
|
+
|
60
|
+
opts.on "-a", "--all", "try all known methods" do
|
61
|
+
@options[:prime] = :all
|
62
|
+
@options[:order] = :all
|
63
|
+
end
|
64
|
+
|
51
65
|
opts.on("-o", "--order X", /all|auto|[bxy,]+/i,
|
52
|
-
"pixel iteration order (default: '#{
|
66
|
+
"pixel iteration order (default: '#{@options[:order]}')",
|
53
67
|
"valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...",
|
54
68
|
){ |x| @options[:order] = x.split(',') }
|
55
69
|
|
@@ -64,6 +78,11 @@ module ZSteg
|
|
64
78
|
opts.on "-q", "--quiet", "Silent any warnings (can be used multiple times)" do |v|
|
65
79
|
@options[:verbose] -= 1
|
66
80
|
end
|
81
|
+
opts.on "-C", "--[no-]color", "Force (or disable) color output (default: auto)" do |x|
|
82
|
+
Sickill::Rainbow.enabled = x
|
83
|
+
end
|
84
|
+
opts.separator "\nPARAMS SHORTCUT\n"+
|
85
|
+
"\tzsteg fname.png 2b,b,lsb,xy ==> --bits 2 --channel b --lsb --order xy"
|
67
86
|
end
|
68
87
|
|
69
88
|
if (argv = optparser.parse(@argv)).empty?
|
@@ -73,12 +92,19 @@ module ZSteg
|
|
73
92
|
|
74
93
|
@actions = DEFAULT_ACTIONS if @actions.empty?
|
75
94
|
|
95
|
+
argv.each do |arg|
|
96
|
+
if arg[','] && !File.exist?(arg)
|
97
|
+
@options.merge!(decode_param_string(arg))
|
98
|
+
argv.delete arg
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
76
102
|
argv.each_with_index do |fname,idx|
|
77
103
|
if argv.size > 1 && @options[:verbose] >= 0
|
78
104
|
puts if idx > 0
|
79
105
|
puts "[.] #{fname}".green
|
80
106
|
end
|
81
|
-
@fname
|
107
|
+
next unless @img=load_image(@fname=fname)
|
82
108
|
|
83
109
|
@actions.each do |action|
|
84
110
|
if action.is_a?(Array)
|
@@ -93,39 +119,70 @@ module ZSteg
|
|
93
119
|
# prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
|
94
120
|
end
|
95
121
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
122
|
+
def load_image fname
|
123
|
+
if File.directory?(fname)
|
124
|
+
puts "[?] #{fname} is a directory".yellow
|
125
|
+
else
|
126
|
+
ZPNG::Image.load(fname)
|
127
|
+
end
|
128
|
+
rescue ZPNG::Exception, Errno::ENOENT
|
129
|
+
puts "[!] #{$!.inspect}".red
|
101
130
|
end
|
102
131
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
132
|
+
def parse_bits x
|
133
|
+
case x
|
134
|
+
when '1', 1 # catch NOT A BINARY MASK early
|
135
|
+
1
|
136
|
+
when /^0x[0-9a-f]+$/i # hex, mask
|
137
|
+
0x100 + x.to_i(16)
|
138
|
+
when /^(?:0b)?[01]+$/i # binary, mask
|
139
|
+
0x100 + x.to_i(2)
|
140
|
+
when /^\d+$/ # decimal, number of bits
|
141
|
+
x.to_i
|
142
|
+
else
|
143
|
+
raise "invalid bits value: #{x.inspect}"
|
108
144
|
end
|
145
|
+
end
|
109
146
|
|
147
|
+
def decode_param_string s
|
110
148
|
h = {}
|
111
|
-
|
149
|
+
s.split(',').each do |x|
|
112
150
|
case x
|
113
151
|
when 'lsb'
|
114
152
|
h[:bit_order] = :lsb
|
115
153
|
when 'msb'
|
116
154
|
h[:bit_order] = :msb
|
117
|
-
when
|
118
|
-
h[:bits] = $1
|
155
|
+
when /^(\d)b$/, /^b(\d)$/
|
156
|
+
h[:bits] = parse_bits($1)
|
119
157
|
when /\A[rgba]+\Z/
|
120
|
-
h[:channels] = x
|
121
|
-
when /\Axy|yx\Z/i
|
158
|
+
h[:channels] = [x]
|
159
|
+
when /\Axy|yx|yb|by\Z/i
|
122
160
|
h[:order] = x
|
161
|
+
when 'prime'
|
162
|
+
h[:prime] = true
|
123
163
|
else
|
124
164
|
raise "uknown param #{x.inspect}"
|
125
165
|
end
|
126
166
|
end
|
127
|
-
h
|
128
|
-
|
167
|
+
h
|
168
|
+
end
|
169
|
+
|
170
|
+
###########################################################################
|
171
|
+
# actions
|
172
|
+
|
173
|
+
def check
|
174
|
+
Checker.new(@img, @options).check
|
175
|
+
end
|
176
|
+
|
177
|
+
def extract name
|
178
|
+
if ['extradata', 'data after IEND'].include?(name)
|
179
|
+
print @img.extradata
|
180
|
+
return
|
181
|
+
end
|
182
|
+
|
183
|
+
h = decode_param_string name
|
184
|
+
h[:limit] = @options[:limit] if @options[:limit] != Checker::DEFAULT_LIMIT
|
185
|
+
print Extractor.new(@img, @options).extract(h)
|
129
186
|
end
|
130
187
|
|
131
188
|
end
|
@@ -5,22 +5,23 @@ module ZSteg
|
|
5
5
|
module ByteExtractor
|
6
6
|
|
7
7
|
def byte_extract params = {}
|
8
|
-
|
9
|
-
limit = 2**32 if limit <= 0
|
10
|
-
|
11
|
-
bits = params[:bits]
|
12
|
-
raise "invalid bits value #{bits.inspect}" unless (1..8).include?(bits)
|
13
|
-
mask = 2**bits - 1
|
8
|
+
masks = bits2masks params[:bits]
|
14
9
|
|
10
|
+
if params[:prime]
|
11
|
+
pregenerate_primes(
|
12
|
+
:max => @image.scanlines[0].size * @image.height,
|
13
|
+
:count => (@limit*8.0/masks.size).ceil
|
14
|
+
)
|
15
|
+
end
|
15
16
|
|
16
17
|
data = ''.force_encoding('binary')
|
17
18
|
a = []
|
18
|
-
byte_iterator(params
|
19
|
+
byte_iterator(params) do |x,y|
|
19
20
|
sl = @image.scanlines[y]
|
20
21
|
|
21
|
-
value = sl.decoded_bytes
|
22
|
-
|
23
|
-
a << ((value &
|
22
|
+
value = sl.decoded_bytes.getbyte(x)
|
23
|
+
masks.each do |mask|
|
24
|
+
a << ((value & mask) == 0 ? 0 : 1)
|
24
25
|
end
|
25
26
|
|
26
27
|
if a.size >= 8
|
@@ -32,8 +33,8 @@ module ZSteg
|
|
32
33
|
end
|
33
34
|
#printf "[d] %02x %08b\n", byte, byte
|
34
35
|
data << byte.chr
|
35
|
-
if data.size >= limit
|
36
|
-
print "[limit
|
36
|
+
if data.size >= @limit
|
37
|
+
print "[limit #@limit]".gray if @verbose > 1
|
37
38
|
break
|
38
39
|
end
|
39
40
|
end
|
@@ -51,7 +52,8 @@ module ZSteg
|
|
51
52
|
# ...
|
52
53
|
# 'xY': b=0, y=MAX; b=1, y=MAX; b=2, y=MAX; ...
|
53
54
|
# 'XY': b=MAX,y=MAX; b=MAX-1,y=MAX; b=MAX-2,y=MAX; ...
|
54
|
-
def byte_iterator
|
55
|
+
def byte_iterator params
|
56
|
+
type = params[:order]
|
55
57
|
if type.nil? || type == 'auto'
|
56
58
|
type = @image.format == :bmp ? 'bY' : 'by'
|
57
59
|
end
|
@@ -59,6 +61,7 @@ module ZSteg
|
|
59
61
|
|
60
62
|
sl0 = @image.scanlines.first
|
61
63
|
|
64
|
+
# XXX don't try to run it on interlaced PNGs!
|
62
65
|
x0,x1,xstep =
|
63
66
|
if type.index('b')
|
64
67
|
[0, sl0.decoded_bytes.size-1, 1]
|
@@ -73,19 +76,31 @@ module ZSteg
|
|
73
76
|
[@image.height-1, 0, -1]
|
74
77
|
end
|
75
78
|
|
79
|
+
# cannot join these lines from ByteExtractor and ColorExtractor into
|
80
|
+
# one method for performance reason:
|
81
|
+
# it will require additional yield() for EACH BYTE iterated
|
82
|
+
|
76
83
|
if type[0,1].downcase == 'b'
|
77
84
|
# ROW iterator
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
if params[:prime]
|
86
|
+
idx = 0
|
87
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x|
|
88
|
+
yield(x,y) if @primes.include?(idx)
|
89
|
+
idx += 1
|
90
|
+
}}
|
91
|
+
else
|
92
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x| yield(x,y) }}
|
82
93
|
end
|
83
94
|
else
|
84
95
|
# COLUMN iterator
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
96
|
+
if params[:prime]
|
97
|
+
idx = 0
|
98
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y|
|
99
|
+
yield(x,y) if @primes.include?(idx)
|
100
|
+
idx += 1
|
101
|
+
}}
|
102
|
+
else
|
103
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y| yield(x,y) }}
|
89
104
|
end
|
90
105
|
end
|
91
106
|
end
|
@@ -4,40 +4,61 @@ module ZSteg
|
|
4
4
|
module ColorExtractor
|
5
5
|
|
6
6
|
def color_extract params = {}
|
7
|
-
channels =
|
7
|
+
channels = Array(params[:channels])
|
8
|
+
#pixel_align = params[:pixel_align]
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
ch_masks = []
|
11
|
+
case channels.first.size
|
12
|
+
when 1
|
13
|
+
# ['r', 'g', 'b']
|
14
|
+
channels.each{ |c| ch_masks << [c[0], bits2masks(params[:bits])] }
|
15
|
+
when 2
|
16
|
+
# ['r3', 'g2', 'b3']
|
17
|
+
channels.each{ |c| ch_masks << [c[0], bits2masks(c[1].to_i)] }
|
18
|
+
else
|
19
|
+
raise "invalid channels: #{channels.inspect}"
|
20
|
+
end
|
11
21
|
|
12
|
-
bits =
|
13
|
-
|
14
|
-
mask = 2**bits - 1
|
22
|
+
# total number of bits = sum of all channels bits
|
23
|
+
nbits = ch_masks.map{ |x| x[1].size }.inject(&:+)
|
15
24
|
|
25
|
+
if params[:prime]
|
26
|
+
pregenerate_primes(
|
27
|
+
:max => @image.width * @image.height,
|
28
|
+
:count => (@limit*8.0/nbits/channels.size).ceil
|
29
|
+
)
|
30
|
+
end
|
16
31
|
|
17
32
|
data = ''.force_encoding('binary')
|
18
33
|
a = []
|
19
|
-
|
20
|
-
|
34
|
+
#puts
|
35
|
+
catch :limit do
|
36
|
+
coord_iterator(params) do |x,y|
|
37
|
+
color = @image[x,y]
|
21
38
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
39
|
+
ch_masks.each do |c,masks|
|
40
|
+
value = color.send(c)
|
41
|
+
masks.each do |mask|
|
42
|
+
a << ((value & mask) == 0 ? 0 : 1)
|
43
|
+
end
|
26
44
|
end
|
27
|
-
|
45
|
+
#p [x,y,a.size,a]
|
28
46
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
while a.size >= 8
|
48
|
+
byte = 0
|
49
|
+
#puts a.join
|
50
|
+
if params[:bit_order] == :msb
|
51
|
+
8.times{ |i| byte |= (a.shift<<i)}
|
52
|
+
else
|
53
|
+
8.times{ |i| byte |= (a.shift<<(7-i))}
|
54
|
+
end
|
55
|
+
#printf "[d] %02x %08b\n", byte, byte
|
56
|
+
data << byte.chr
|
57
|
+
if data.size >= @limit
|
58
|
+
print "[limit #@limit]".gray if @verbose > 1
|
59
|
+
throw :limit
|
60
|
+
end
|
61
|
+
#a.clear if pixel_align
|
41
62
|
end
|
42
63
|
end
|
43
64
|
end
|
@@ -54,7 +75,8 @@ module ZSteg
|
|
54
75
|
# ...
|
55
76
|
# 'xY': x=0, y=MAX; x=1, y=MAX; x=2, y=MAX; ...
|
56
77
|
# 'XY': x=MAX,y=MAX; x=MAX-1,y=MAX; x=MAX-2,y=MAX; ...
|
57
|
-
def coord_iterator
|
78
|
+
def coord_iterator params
|
79
|
+
type = params[:order]
|
58
80
|
if type.nil? || type == 'auto'
|
59
81
|
type = @image.format == :bmp ? 'xY' : 'xy'
|
60
82
|
end
|
@@ -74,19 +96,31 @@ module ZSteg
|
|
74
96
|
[@image.height-1, 0, -1]
|
75
97
|
end
|
76
98
|
|
99
|
+
# cannot join these lines from ByteExtractor and ColorExtractor into
|
100
|
+
# one method for performance reason:
|
101
|
+
# it will require additional yield() for EACH BYTE iterated
|
102
|
+
|
77
103
|
if type[0,1].downcase == 'x'
|
78
104
|
# ROW iterator
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
105
|
+
if params[:prime]
|
106
|
+
idx = 0
|
107
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x|
|
108
|
+
yield(x,y) if @primes.include?(idx)
|
109
|
+
idx += 1
|
110
|
+
}}
|
111
|
+
else
|
112
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x| yield(x,y) }}
|
83
113
|
end
|
84
114
|
else
|
85
115
|
# COLUMN iterator
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
116
|
+
if params[:prime]
|
117
|
+
idx = 0
|
118
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y|
|
119
|
+
yield(x,y) if @primes.include?(idx)
|
120
|
+
idx += 1
|
121
|
+
}}
|
122
|
+
else
|
123
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y| yield(x,y) }}
|
90
124
|
end
|
91
125
|
end
|
92
126
|
end
|
data/lib/zsteg/extractor.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'prime'
|
2
|
+
require 'set'
|
3
|
+
|
1
4
|
module ZSteg
|
2
5
|
class Extractor
|
3
6
|
|
@@ -7,15 +10,47 @@ module ZSteg
|
|
7
10
|
# image can be either filename or ZPNG::Image
|
8
11
|
def initialize image, params = {}
|
9
12
|
@image = image.is_a?(ZPNG::Image) ? image : ZPNG::Image.load(image)
|
10
|
-
@verbose = params[:verbose]
|
13
|
+
@verbose = params[:verbose] || 0
|
11
14
|
end
|
12
15
|
|
13
16
|
def extract params = {}
|
17
|
+
@limit = params[:limit].to_i
|
18
|
+
@limit = 2**32 if @limit <= 0
|
19
|
+
|
14
20
|
if params[:order] =~ /b/i
|
15
21
|
byte_extract params
|
16
22
|
else
|
17
23
|
color_extract params
|
18
24
|
end
|
19
25
|
end
|
26
|
+
|
27
|
+
def pregenerate_primes h
|
28
|
+
@primes ||= Set.new
|
29
|
+
return if @primes.size >= h[:count]
|
30
|
+
|
31
|
+
count = h[:count]
|
32
|
+
Prime.each(h[:max]) do |prime|
|
33
|
+
@primes << prime
|
34
|
+
break if @primes.size >= count
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def bits2masks bits
|
39
|
+
masks = []
|
40
|
+
if (1..8).include?(bits)
|
41
|
+
# number of bits
|
42
|
+
bits.times do |i|
|
43
|
+
masks << (1<<(bits-i-1))
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# mask
|
47
|
+
bits &= 0xff
|
48
|
+
8.times do |i|
|
49
|
+
mask = (1<<(bits-i-1))
|
50
|
+
masks << mask if (bits & mask) != 0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
masks
|
54
|
+
end
|
20
55
|
end
|
21
56
|
end
|