zpng 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +0 -1
- data/Gemfile.lock +0 -2
- data/README.md +50 -10
- data/README.md.tpl +15 -1
- data/TODO +6 -0
- data/VERSION +1 -1
- data/lib/zpng.rb +4 -32
- data/lib/zpng/adam7_decoder.rb +8 -1
- data/lib/zpng/chunk.rb +7 -24
- data/lib/zpng/cli.rb +196 -141
- data/lib/zpng/color.rb +85 -30
- data/lib/zpng/hexdump.rb +86 -0
- data/lib/zpng/image.rb +99 -21
- data/lib/zpng/metadata.rb +20 -0
- data/lib/zpng/pixels.rb +25 -0
- data/lib/zpng/scan_line.rb +139 -87
- data/lib/zpng/string_ext.rb +9 -1
- data/lib/zpng/text_chunk.rb +75 -0
- data/samples/itxt.png +0 -0
- data/spec/adam7_spec.rb +24 -0
- data/spec/alpha_spec.rb +28 -0
- data/spec/cli_spec.rb +62 -0
- data/spec/color_spec.rb +12 -2
- data/spec/deinterlace_spec.rb +19 -0
- data/spec/metadata_spec.rb +22 -0
- data/spec/pixel_access_spec.rb +16 -0
- data/spec/pixels_enumerator_spec.rb +34 -0
- data/spec/set_random_pixel_spec.rb +13 -0
- data/spec/spec_helper.rb +1 -22
- data/spec/support/png_suite.rb +43 -0
- data/zpng.gemspec +15 -5
- metadata +16 -19
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -3,7 +3,6 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
diff-lcs (1.1.3)
|
5
5
|
git (1.2.5)
|
6
|
-
hexdump (0.2.3)
|
7
6
|
jeweler (1.8.4)
|
8
7
|
bundler (~> 1.0)
|
9
8
|
git (>= 1.2.5)
|
@@ -28,7 +27,6 @@ PLATFORMS
|
|
28
27
|
|
29
28
|
DEPENDENCIES
|
30
29
|
bundler (>= 1.0.0)
|
31
|
-
hexdump
|
32
30
|
jeweler (~> 1.8.4)
|
33
31
|
rainbow
|
34
32
|
rspec (>= 2.8.0)
|
data/README.md
CHANGED
@@ -6,52 +6,91 @@ Description
|
|
6
6
|
-----------
|
7
7
|
A pure ruby PNG file manipulation & validation
|
8
8
|
|
9
|
+
(If you need a high-level PNG creation toolkit - take a look at [SugarPNG](https://github.com/zed-0xff/sugar_png))
|
10
|
+
|
9
11
|
Installation
|
10
12
|
------------
|
11
13
|
gem install zpng
|
12
14
|
|
15
|
+
Comparison
|
16
|
+
----------
|
17
|
+
* supports `iTXt` (international text) chunks
|
18
|
+
* full support of 16-bit color & alpha depth
|
19
|
+
* correct 4bpp image decoding (as of 29-Dec-2012, ChunkyPNG had 1-bit error in 4bpp image decoding)
|
20
|
+
|
13
21
|
Usage
|
14
22
|
-----
|
15
23
|
|
16
24
|
# zpng -h
|
17
25
|
|
18
26
|
Usage: zpng [options] filename.png
|
19
|
-
|
20
|
-
-q, --quiet Silent any warnings (can be used multiple times)
|
21
|
-
-C, --chunks Show file chunks (default)
|
27
|
+
|
22
28
|
-i, --info General image info (default)
|
29
|
+
-c, --chunk(s) [ID] Show chunks (default) or single chunk by its #
|
30
|
+
-m, --metadata Show image metadata, if any (default)
|
31
|
+
|
23
32
|
-S, --scanlines Show scanlines info
|
24
33
|
-P, --palette Show palette
|
25
34
|
-E, --extract-chunk ID extract a single chunk
|
26
35
|
-D, --imagedata dump unpacked Image Data (IDAT) chunk(s) to stdout
|
27
36
|
|
28
|
-
-
|
37
|
+
-C, --crop GEOMETRY crop image, {WIDTH}x{HEIGHT}+{X}+{Y},
|
29
38
|
puts results on stdout unless --ascii given
|
30
39
|
|
31
40
|
-A, --ascii Try to convert image to ASCII (works best with monochrome images)
|
32
41
|
-N, --ansi Try to display image as ANSI colored text
|
33
42
|
-2, --256 Try to display image as 256-colored text
|
34
43
|
-W, --wide Use 2 horizontal characters per one pixel
|
44
|
+
|
45
|
+
-v, --verbose Run verbosely (can be used multiple times)
|
46
|
+
-q, --quiet Silent any warnings (can be used multiple times)
|
35
47
|
|
36
48
|
### Info
|
37
49
|
|
38
|
-
# zpng
|
50
|
+
# zpng qr_rgb.png
|
39
51
|
|
40
|
-
[.] image size 35x35
|
52
|
+
[.] image size 35x35, 24bpp, COLOR_RGB
|
41
53
|
[.] uncompressed imagedata size = 3710 bytes
|
54
|
+
[.] <Chunk #00 IHDR size= 13, crc=91bb240e, width=35, color=2, interlace=0, depth=8, compression=0, height=35, filter=0> CRC OK
|
55
|
+
[.] <Chunk #01 sRGB size= 1, crc=aece1ce9 > CRC OK
|
56
|
+
[.] <Chunk #02 IDAT size= 399, crc=59790716 > CRC OK
|
57
|
+
[.] <Chunk #03 IEND size= 0, crc=ae426082 > CRC OK
|
58
|
+
|
59
|
+
### Info (verbose)
|
60
|
+
|
61
|
+
# zpng -v qr_rgb.png
|
62
|
+
|
63
|
+
[.] image size 35x35, 24bpp, COLOR_RGB
|
64
|
+
[.] uncompressed imagedata size = 3710 bytes
|
65
|
+
01 ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
66
|
+
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 3678 bytes
|
67
|
+
|
68
|
+
[.] <Chunk #00 IHDR size= 13, crc=91bb240e, width=35, idx=0, color=2, interlace=0, depth=8, compression=0, height=35, filter=0> CRC OK
|
69
|
+
00 00 00 23 00 00 00 23 08 02 00 00 00 |...#...#..... |
|
70
|
+
|
71
|
+
[.] <Chunk #01 sRGB size= 1, crc=aece1ce9 > CRC OK
|
72
|
+
00 |. |
|
73
|
+
|
74
|
+
[.] <Chunk #02 IDAT size= 399, crc=59790716 > CRC OK
|
75
|
+
48 c7 bd 56 41 12 c4 20 08 d3 8e ff ff b2 7b 70 |H..VA.. ......{p|
|
76
|
+
86 d2 24 44 db c3 7a d8 d9 b6 08 18 03 a1 cf 39 |..$D..z........9| + 367 bytes
|
77
|
+
|
78
|
+
[.] <Chunk #03 IEND size= 0, crc=ae426082 > CRC OK
|
79
|
+
|
80
|
+
( add more `-v`'s for even more verbose output)
|
42
81
|
|
43
82
|
### Chunks
|
44
83
|
|
45
84
|
# zpng --chunks qr_aux_chunks.png
|
46
85
|
|
47
|
-
[.] <Chunk #00 IHDR size= 13, crc=36a28ef4, width=35,
|
86
|
+
[.] <Chunk #00 IHDR size= 13, crc=36a28ef4, width=35, color=0, interlace=0, depth=1, compression=0, height=35, filter=0> CRC OK
|
48
87
|
[.] <Chunk #01 gAMA size= 4, crc=0bfc6105 > CRC OK
|
49
88
|
[.] <Chunk #02 sRGB size= 1, crc=aece1ce9 > CRC OK
|
50
89
|
[.] <Chunk #03 cHRM size= 32, crc=9cba513c > CRC OK
|
51
90
|
[.] <Chunk #04 pHYs size= 9, crc=46c96b3e > CRC OK
|
52
91
|
[.] <Chunk #05 IDAT size= 213, crc=5f3f1ff9 > CRC OK
|
53
|
-
[.] <Chunk #06 tEXt size= 37, crc=8d62fd1a > CRC OK
|
54
|
-
[.] <Chunk #07 tEXt size= 37, crc=fc3f45a6 > CRC OK
|
92
|
+
[.] <Chunk #06 tEXt size= 37, crc=8d62fd1a, keyword="date:create"> CRC OK
|
93
|
+
[.] <Chunk #07 tEXt size= 37, crc=fc3f45a6, keyword="date:modify"> CRC OK
|
55
94
|
[.] <Chunk #08 IEND size= 0, crc=ae426082 > CRC OK
|
56
95
|
|
57
96
|
### ASCII
|
@@ -141,7 +180,8 @@ source image: ![qr_rgb.png](https://github.com/zed-0xff/zpng/raw/master/samples/
|
|
141
180
|
# zpng --palette qr_plte_bw.png
|
142
181
|
|
143
182
|
<Chunk #02 PLTE size= 6, crc=55c2d37e >
|
144
|
-
|
183
|
+
color #0: ff ff ff |...|
|
184
|
+
color #1: 00 00 00 |...|
|
145
185
|
|
146
186
|
|
147
187
|
## Image manipulation
|
data/README.md.tpl
CHANGED
@@ -6,10 +6,18 @@ Description
|
|
6
6
|
-----------
|
7
7
|
A pure ruby PNG file manipulation & validation
|
8
8
|
|
9
|
+
(If you need a high-level PNG creation toolkit - take a look at [SugarPNG](https://github.com/zed-0xff/sugar_png))
|
10
|
+
|
9
11
|
Installation
|
10
12
|
------------
|
11
13
|
gem install zpng
|
12
14
|
|
15
|
+
Comparison
|
16
|
+
----------
|
17
|
+
* supports `iTXt` (international text) chunks
|
18
|
+
* full support of 16-bit color & alpha depth
|
19
|
+
* correct 4bpp image decoding (as of 29-Dec-2012, ChunkyPNG had 1-bit error in 4bpp image decoding)
|
20
|
+
|
13
21
|
Usage
|
14
22
|
-----
|
15
23
|
|
@@ -17,7 +25,13 @@ Usage
|
|
17
25
|
|
18
26
|
### Info
|
19
27
|
|
20
|
-
% zpng
|
28
|
+
% zpng qr_rgb.png
|
29
|
+
|
30
|
+
### Info (verbose)
|
31
|
+
|
32
|
+
% zpng -v qr_rgb.png
|
33
|
+
|
34
|
+
( add more `-v`'s for even more verbose output)
|
21
35
|
|
22
36
|
### Chunks
|
23
37
|
|
data/TODO
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/lib/zpng.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
require 'zlib'
|
3
2
|
require 'stringio'
|
4
3
|
|
@@ -9,36 +8,9 @@ require 'zpng/color'
|
|
9
8
|
require 'zpng/block'
|
10
9
|
require 'zpng/scan_line'
|
11
10
|
require 'zpng/chunk'
|
11
|
+
require 'zpng/text_chunk'
|
12
12
|
require 'zpng/image'
|
13
13
|
require 'zpng/adam7_decoder'
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
if $0 == __FILE__
|
18
|
-
if ARGV.size == 0
|
19
|
-
puts "gimme a png filename!"
|
20
|
-
else
|
21
|
-
img = ZPNG::Image.new(ARGV[0])
|
22
|
-
img.dump
|
23
|
-
puts "[.] image size #{img.width}x#{img.height}"
|
24
|
-
puts "[.] uncompressed imagedata size=#{img.imagedata.size}"
|
25
|
-
puts "[.] palette =#{img.palette}"
|
26
|
-
# puts "[.] imagedata: #{img.imagedata[0..30].split('').map{|x| sprintf("%02X",x.ord)}.join(' ')}"
|
27
|
-
|
28
|
-
require 'hexdump'
|
29
|
-
#Hexdump.dump(img.imagedata, :width => 6)
|
30
|
-
require 'pp'
|
31
|
-
pp img.scanlines[0..5]
|
32
|
-
# puts Hexdump.dump(img.imagedata[0,60])
|
33
|
-
# img.scanlines.each do |l|
|
34
|
-
# puts l.to_s
|
35
|
-
# end
|
36
|
-
puts img.to_s
|
37
|
-
img[1,0]= ZPNG::Color.new(0,0,0)
|
38
|
-
puts img.to_s
|
39
|
-
File.open 'export.png','wb' do |f|
|
40
|
-
f << img.export
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
14
|
+
require 'zpng/hexdump'
|
15
|
+
require 'zpng/metadata'
|
16
|
+
require 'zpng/pixels'
|
data/lib/zpng/adam7_decoder.rb
CHANGED
@@ -18,7 +18,7 @@ module ZPNG
|
|
18
18
|
@scanlines_count = 0
|
19
19
|
# two leading zeroes added specially for convert_coords() code readability
|
20
20
|
@pass_starts = [0,0] + @widths.map(&:size).map{ |x| @scanlines_count+=x }
|
21
|
-
@widths.flatten!
|
21
|
+
@widths.flatten! # yahoo! :))
|
22
22
|
end
|
23
23
|
|
24
24
|
# scanline width in pixels
|
@@ -67,5 +67,12 @@ module ZPNG
|
|
67
67
|
raise "invalid coords"
|
68
68
|
end
|
69
69
|
end
|
70
|
+
|
71
|
+
# is the specified scanline is a first scanline in the pass?
|
72
|
+
# When the image is interlaced, each pass of the interlace pattern is
|
73
|
+
# treated as an independent image for filtering purposes
|
74
|
+
def pass_start? idx
|
75
|
+
@pass_starts.include?(idx)
|
76
|
+
end
|
70
77
|
end
|
71
78
|
end
|
data/lib/zpng/chunk.rb
CHANGED
@@ -54,7 +54,7 @@ module ZPNG
|
|
54
54
|
@data || ''
|
55
55
|
end
|
56
56
|
|
57
|
-
def inspect
|
57
|
+
def inspect verbosity = 10
|
58
58
|
size = @size ? sprintf("%6d",@size) : sprintf("%6s","???")
|
59
59
|
crc = @crc ? sprintf("%08x",@crc) : sprintf("%8s","???")
|
60
60
|
type = @type.to_s.gsub(/[^0-9a-z]/i){ |x| sprintf("\\x%02X",x.ord) }
|
@@ -180,10 +180,11 @@ module ZPNG
|
|
180
180
|
(@color & ALPHA_USED) != 0
|
181
181
|
end
|
182
182
|
|
183
|
-
def inspect
|
183
|
+
def inspect verbosity = 10
|
184
|
+
vars = instance_variables - [:@type, :@crc, :@data, :@size]
|
185
|
+
vars -= [:@idx] if verbosity <= 0
|
184
186
|
super.sub(/ *>$/,'') + ", " +
|
185
|
-
|
186
|
-
map{ |var| "#{var.to_s.tr('@','')}=#{instance_variable_get(var)}" }.
|
187
|
+
vars.map{ |var| "#{var.to_s.tr('@','')}=#{instance_variable_get(var)}" }.
|
187
188
|
join(", ") + ">"
|
188
189
|
end
|
189
190
|
end
|
@@ -214,7 +215,7 @@ module ZPNG
|
|
214
215
|
end
|
215
216
|
|
216
217
|
def add color
|
217
|
-
raise "palette full, cannot add
|
218
|
+
raise "palette full (#{ncolors}), cannot add #{color.inspect}" if ncolors >= max_colors
|
218
219
|
idx = ncolors
|
219
220
|
self[idx] = color
|
220
221
|
idx
|
@@ -228,25 +229,7 @@ module ZPNG
|
|
228
229
|
|
229
230
|
class IDAT < Chunk; end
|
230
231
|
class IEND < Chunk; end
|
232
|
+
class TRNS < Chunk; end
|
231
233
|
|
232
|
-
class ZTXT < Chunk
|
233
|
-
attr_accessor :keyword, :comp_method, :text
|
234
|
-
def initialize *args
|
235
|
-
super
|
236
|
-
@keyword,@comp_method,@text = data.unpack('Z*Ca*')
|
237
|
-
if @text
|
238
|
-
@text = Zlib::Inflate.inflate(@text)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
def inspect
|
242
|
-
super.sub(/ *>$/,'') + ", " +
|
243
|
-
(instance_variables-[:@type, :@crc, :@data, :@size]).
|
244
|
-
map do |var|
|
245
|
-
t = instance_variable_get(var).to_s
|
246
|
-
t = t[0..10] + "..." if t.size > 10
|
247
|
-
"#{var.to_s.tr('@','')}=#{t}"
|
248
|
-
end.join(", ") + ">"
|
249
|
-
end
|
250
|
-
end
|
251
234
|
end
|
252
235
|
end
|
data/lib/zpng/cli.rb
CHANGED
@@ -1,193 +1,248 @@
|
|
1
1
|
require 'zpng'
|
2
2
|
require 'optparse'
|
3
|
-
require 'hexdump'
|
4
3
|
require 'pp'
|
5
4
|
|
6
|
-
|
5
|
+
module ZPNG
|
6
|
+
class CLI
|
7
|
+
include Hexdump
|
7
8
|
|
8
|
-
|
9
|
-
'chunks' => 'Show file chunks (default)',
|
10
|
-
%w'i info' => 'General image info (default)',
|
11
|
-
'scanlines' => 'Show scanlines info',
|
12
|
-
'palette' => 'Show palette'
|
13
|
-
}
|
14
|
-
DEFAULT_ACTIONS = %w'info chunks'
|
9
|
+
DEFAULT_ACTIONS = %w'info metadata chunks'
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
def run
|
21
|
-
@actions = []
|
22
|
-
@options = { :verbose => 0 }
|
23
|
-
optparser = OptionParser.new do |opts|
|
24
|
-
opts.banner = "Usage: zpng [options] filename.png"
|
11
|
+
def initialize argv = ARGV
|
12
|
+
# hack #1: allow --chunk as well as --chunks
|
13
|
+
@argv = argv.map{ |x| x.sub(/^--chunks?/,'--chunk(s)') }
|
25
14
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
15
|
+
# hack #2: allow --chunk(s) followed by a non-number, like "zpng --chunks fname.png"
|
16
|
+
@argv.each_cons(2) do |a,b|
|
17
|
+
if a == "--chunk(s)" && b !~ /^\d+$/
|
18
|
+
a<<"=-1"
|
19
|
+
end
|
31
20
|
end
|
21
|
+
end
|
32
22
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
23
|
+
def run
|
24
|
+
@actions = []
|
25
|
+
@options = { :verbose => 0 }
|
26
|
+
optparser = OptionParser.new do |opts|
|
27
|
+
opts.banner = "Usage: zpng [options] filename.png"
|
28
|
+
opts.separator ""
|
29
|
+
|
30
|
+
opts.on("-i", "--info", "General image info (default)"){ @actions << :info }
|
31
|
+
opts.on("-c", "--chunk(s) [ID]", Integer, "Show chunks (default) or single chunk by its #") do |id|
|
32
|
+
id = nil if id == -1
|
33
|
+
@actions << [:chunks, id]
|
38
34
|
end
|
39
|
-
|
35
|
+
opts.on("-m", "--metadata", "Show image metadata, if any (default)"){ @actions << :metadata }
|
40
36
|
|
41
|
-
|
42
|
-
@actions <<
|
43
|
-
|
44
|
-
opts.on "-D", "--imagedata", "dump unpacked Image Data (IDAT) chunk(s) to stdout" do
|
45
|
-
@actions << :unpack_imagedata
|
46
|
-
end
|
37
|
+
opts.separator ""
|
38
|
+
opts.on("-S", "--scanlines", "Show scanlines info"){ @actions << :scanlines }
|
39
|
+
opts.on("-P", "--palette", "Show palette"){ @actions << :palette }
|
47
40
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
41
|
+
opts.on "-E", "--extract-chunk ID", Integer, "extract a single chunk" do |id|
|
42
|
+
@actions << [:extract_chunk, id]
|
43
|
+
end
|
44
|
+
opts.on "-D", "--imagedata", "dump unpacked Image Data (IDAT) chunk(s) to stdout" do
|
45
|
+
@actions << :unpack_imagedata
|
46
|
+
end
|
53
47
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
48
|
+
opts.separator ""
|
49
|
+
opts.on "-C", "--crop GEOMETRY", "crop image, {WIDTH}x{HEIGHT}+{X}+{Y},",
|
50
|
+
"puts results on stdout unless --ascii given" do |x|
|
51
|
+
@actions << [:crop, x]
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.separator ""
|
55
|
+
opts.on "-A", '--ascii', 'Try to convert image to ASCII (works best with monochrome images)' do
|
56
|
+
@actions << :ascii
|
57
|
+
end
|
58
|
+
opts.on "-N", '--ansi', 'Try to display image as ANSI colored text' do
|
59
|
+
@actions << :ansi
|
60
|
+
end
|
61
|
+
opts.on "-2", '--256', 'Try to display image as 256-colored text' do
|
62
|
+
@actions << :ansi256
|
63
|
+
end
|
64
|
+
opts.on "-W", '--wide', 'Use 2 horizontal characters per one pixel' do
|
65
|
+
@options[:wide] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.separator ""
|
69
|
+
opts.on "-v", "--verbose", "Run verbosely (can be used multiple times)" do |v|
|
70
|
+
@options[:verbose] += 1
|
71
|
+
end
|
72
|
+
opts.on "-q", "--quiet", "Silent any warnings (can be used multiple times)" do |v|
|
73
|
+
@options[:verbose] -= 1
|
74
|
+
end
|
60
75
|
end
|
61
|
-
|
62
|
-
|
76
|
+
|
77
|
+
if (argv = optparser.parse(@argv)).empty?
|
78
|
+
puts optparser.help
|
79
|
+
return
|
63
80
|
end
|
64
|
-
|
65
|
-
|
81
|
+
|
82
|
+
@actions = DEFAULT_ACTIONS if @actions.empty?
|
83
|
+
|
84
|
+
argv.each_with_index do |fname,idx|
|
85
|
+
if argv.size > 1 && @options[:verbose] >= 0
|
86
|
+
puts if idx > 0
|
87
|
+
puts "[.] #{fname}".color(:green)
|
88
|
+
end
|
89
|
+
@file_idx = idx
|
90
|
+
@file_name = fname
|
91
|
+
|
92
|
+
@zpng = load_file fname
|
93
|
+
|
94
|
+
@actions.each do |action|
|
95
|
+
if action.is_a?(Array)
|
96
|
+
self.send(*action) if self.respond_to?(action.first)
|
97
|
+
else
|
98
|
+
self.send(action) if self.respond_to?(action)
|
99
|
+
end
|
100
|
+
end
|
66
101
|
end
|
102
|
+
rescue Errno::EPIPE
|
103
|
+
# output interrupt, f.ex. when piping output to a 'head' command
|
104
|
+
# prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
|
67
105
|
end
|
68
106
|
|
69
|
-
|
70
|
-
|
71
|
-
|
107
|
+
def extract_chunk id
|
108
|
+
@img.chunks.each do |chunk|
|
109
|
+
if chunk.idx == id
|
110
|
+
case chunk
|
111
|
+
when Chunk::ZTXT
|
112
|
+
print chunk.text
|
113
|
+
else
|
114
|
+
print chunk.data
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
72
118
|
end
|
73
119
|
|
74
|
-
|
120
|
+
def unpack_imagedata
|
121
|
+
print @img.imagedata
|
122
|
+
end
|
75
123
|
|
76
|
-
|
77
|
-
|
78
|
-
puts
|
79
|
-
|
124
|
+
def crop geometry
|
125
|
+
unless geometry =~ /\A(\d+)x(\d+)\+(\d+)\+(\d+)\Z/i
|
126
|
+
STDERR.puts "[!] invalid geometry #{geometry.inspect}, must be WxH+X+Y, like 100x100+10+10"
|
127
|
+
exit 1
|
80
128
|
end
|
81
|
-
@
|
82
|
-
@
|
129
|
+
@img.crop! :width => $1.to_i, :height => $2.to_i, :x => $3.to_i, :y => $4.to_i
|
130
|
+
print @img.export unless @actions.include?(:ascii)
|
131
|
+
end
|
83
132
|
|
84
|
-
|
133
|
+
def load_file fname
|
134
|
+
@img = Image.load fname
|
135
|
+
end
|
85
136
|
|
86
|
-
|
87
|
-
|
88
|
-
|
137
|
+
def metadata
|
138
|
+
return if @img.metadata.empty?
|
139
|
+
puts "[.] metadata:"
|
140
|
+
@img.metadata.each do |k,v,h|
|
141
|
+
if h.keys.sort == [:keyword, :text]
|
142
|
+
v.gsub!(/[\n\r]+/, "\n"+" "*18)
|
143
|
+
printf " %-11s : %s\n", k, v.gray
|
89
144
|
else
|
90
|
-
|
145
|
+
printf " %s (%s: %s):", k, h[:language], h[:translated_keyword]
|
146
|
+
v.gsub!(/[\n\r]+/, "\n"+" "*18)
|
147
|
+
printf "\n%s%s\n", " "*18, v.gray
|
91
148
|
end
|
92
149
|
end
|
150
|
+
puts
|
93
151
|
end
|
94
|
-
rescue Errno::EPIPE
|
95
|
-
# output interrupt, f.ex. when piping output to a 'head' command
|
96
|
-
# prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
|
97
|
-
end
|
98
152
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
case chunk
|
103
|
-
when ZPNG::Chunk::ZTXT
|
104
|
-
print chunk.text
|
105
|
-
else
|
106
|
-
print chunk.data
|
107
|
-
end
|
153
|
+
def info
|
154
|
+
color = %w'COLOR_GRAYSCALE COLOR_RGB COLOR_INDEXED COLOR_GRAY_ALPHA COLOR_RGBA'.find do |k|
|
155
|
+
@img.hdr.color == ZPNG.const_get(k)
|
108
156
|
end
|
157
|
+
puts "[.] image size #{@img.width || '?'}x#{@img.height || '?'}, #{@img.bpp}bpp, #{color}"
|
158
|
+
puts "[.] palette = #{@img.palette}" if @img.palette
|
159
|
+
puts "[.] uncompressed imagedata size = #{@img.imagedata.size} bytes"
|
160
|
+
_conditional_hexdump @img.imagedata, 3
|
109
161
|
end
|
110
|
-
end
|
111
162
|
|
112
|
-
|
113
|
-
|
114
|
-
|
163
|
+
def _conditional_hexdump data, v2 = 2
|
164
|
+
if @options[:verbose] <= 0
|
165
|
+
# do nothing
|
166
|
+
elsif @options[:verbose] < v2
|
167
|
+
sz = 0x20
|
168
|
+
print Hexdump.dump(data[0,sz],
|
169
|
+
:show_offset => false,
|
170
|
+
:tail => data.size > sz ? " + #{data.size-sz} bytes\n" : "\n"
|
171
|
+
){ |row| row.insert(0," ") }.gray
|
172
|
+
puts
|
115
173
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
174
|
+
elsif @options[:verbose] >= v2
|
175
|
+
print Hexdump.dump(data){ |row| row.insert(0," ") }.gray
|
176
|
+
puts
|
177
|
+
end
|
120
178
|
end
|
121
|
-
@img.crop! :width => $1.to_i, :height => $2.to_i, :x => $3.to_i, :y => $4.to_i
|
122
|
-
print @img.export unless @actions.include?(:ascii)
|
123
|
-
end
|
124
|
-
|
125
|
-
def load_file fname
|
126
|
-
@img = ZPNG::Image.new fname
|
127
|
-
end
|
128
|
-
|
129
|
-
def info
|
130
|
-
puts "[.] image size #{@img.width || '?'}x#{@img.height || '?'}, bpp=#{@img.bpp}"
|
131
|
-
puts "[.] uncompressed imagedata size = #{@img.imagedata.size} bytes"
|
132
|
-
puts "[.] palette = #{@img.palette}" if @img.palette
|
133
|
-
end
|
134
179
|
|
135
|
-
|
136
|
-
|
137
|
-
|
180
|
+
def chunks idx=nil
|
181
|
+
@img.chunks.each do |chunk|
|
182
|
+
next if idx && chunk.idx != idx
|
183
|
+
colored_type = chunk.type.magenta
|
184
|
+
colored_crc = chunk.crc_ok? ? 'CRC OK'.green : 'CRC ERROR'.red
|
185
|
+
puts "[.] #{chunk.inspect(@options[:verbose]).sub(chunk.type, colored_type)} #{colored_crc}"
|
138
186
|
|
139
|
-
|
140
|
-
@img.height.times do |y|
|
141
|
-
@img.width.times do |x|
|
142
|
-
c = @img[x,y].to_ascii
|
143
|
-
c *= 2 if @options[:wide]
|
144
|
-
print c
|
187
|
+
_conditional_hexdump(chunk.data) unless chunk.size == 0
|
145
188
|
end
|
146
|
-
puts
|
147
189
|
end
|
148
|
-
end
|
149
190
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
191
|
+
def ascii
|
192
|
+
@img.height.times do |y|
|
193
|
+
@img.width.times do |x|
|
194
|
+
c = @img[x,y].to_ascii
|
195
|
+
c *= 2 if @options[:wide]
|
196
|
+
print c
|
197
|
+
end
|
198
|
+
puts
|
155
199
|
end
|
156
|
-
puts
|
157
200
|
end
|
158
|
-
end
|
159
201
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
202
|
+
def ansi
|
203
|
+
spc = @options[:wide] ? " " : " "
|
204
|
+
@img.height.times do |y|
|
205
|
+
@img.width.times do |x|
|
206
|
+
print spc.background(@img[x,y].to_ansi)
|
207
|
+
end
|
208
|
+
puts
|
166
209
|
end
|
167
|
-
puts
|
168
210
|
end
|
169
|
-
end
|
170
211
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
Hexdump.dump(sl.decoded_bytes)
|
179
|
-
when 3..999
|
180
|
-
Hexdump.dump(sl.raw_data)
|
181
|
-
Hexdump.dump(sl.decoded_bytes)
|
212
|
+
def ansi256
|
213
|
+
require 'rainbow'
|
214
|
+
spc = @options[:wide] ? " " : " "
|
215
|
+
@img.height.times do |y|
|
216
|
+
@img.width.times do |x|
|
217
|
+
print spc.background(@img[x,y].to_html)
|
218
|
+
end
|
182
219
|
puts
|
183
220
|
end
|
184
221
|
end
|
185
|
-
end
|
186
222
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
223
|
+
def scanlines
|
224
|
+
@img.scanlines.each do |sl|
|
225
|
+
p sl
|
226
|
+
case @options[:verbose]
|
227
|
+
when 1
|
228
|
+
hexdump(sl.raw_data)
|
229
|
+
when 2
|
230
|
+
hexdump(sl.decoded_bytes)
|
231
|
+
when 3..999
|
232
|
+
hexdump(sl.raw_data)
|
233
|
+
hexdump(sl.decoded_bytes)
|
234
|
+
puts
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def palette
|
240
|
+
if @img.palette
|
241
|
+
pp @img.palette
|
242
|
+
hexdump(@img.palette.data, :width => 3, :show_offset => false) do |row, offset|
|
243
|
+
row.insert(0," color %4s: " % "##{(offset/3)}")
|
244
|
+
end
|
245
|
+
end
|
191
246
|
end
|
192
247
|
end
|
193
248
|
end
|